Codebase list node-d3-shape / f82ae92
Import Upstream version 1.2.0 Pirate Praveen 6 years ago
141 changed file(s) with 5541 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 parserOptions:
1 sourceType: module
2
3 extends:
4 "eslint:recommended"
5
6 rules:
7 no-cond-assign: 0
8 no-fallthrough: 0
0 *.sublime-workspace
1 .DS_Store
2 build/
3 node_modules
4 npm-debug.log
0 *.sublime-*
1 build/*.zip
2 img/
3 test/
0 Copyright 2010-2015 Mike Bostock
1 All rights reserved.
2
3 Redistribution and use in source and binary forms, with or without modification,
4 are permitted provided that the following conditions are met:
5
6 * Redistributions of source code must retain the above copyright notice, this
7 list of conditions and the following disclaimer.
8
9 * Redistributions in binary form must reproduce the above copyright notice,
10 this list of conditions and the following disclaimer in the documentation
11 and/or other materials provided with the distribution.
12
13 * Neither the name of the author nor the names of contributors may be used to
14 endorse or promote products derived from this software without specific prior
15 written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0 # d3-shape
1
2 Visualizations typically consist of discrete graphical marks, such as [symbols](#symbols), [arcs](#arcs), [lines](#lines) and [areas](#areas). While the rectangles of a bar chart may be easy enough to generate directly using [SVG](http://www.w3.org/TR/SVG/paths.html#PathData) or [Canvas](http://www.w3.org/TR/2dcontext/#canvaspathmethods), 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.
3
4 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](https://github.com/d3/d3-scale) fields of your data to fit the chart:
5
6 ```js
7 var line = d3.line()
8 .x(function(d) { return x(d.date); })
9 .y(function(d) { return y(d.value); });
10 ```
11
12 This line generator can then be used to compute the `d` attribute of an SVG path element:
13
14 ```js
15 path.datum(data).attr("d", line);
16 ```
17
18 Or you can use it to render to a Canvas 2D context:
19
20 ```js
21 line.context(context)(data);
22 ```
23
24 For more, read [Introducing d3-shape](https://medium.com/@mbostock/introducing-d3-shape-73f8367e6d12).
25
26 ## Installing
27
28 If you use NPM, `npm install d3-shape`. Otherwise, download the [latest release](https://github.com/d3/d3-shape/releases/latest). You can also load directly from [d3js.org](https://d3js.org), either as a [standalone library](https://d3js.org/d3-shape.v1.min.js) or as part of [D3 4.0](https://github.com/d3/d3). AMD, CommonJS, and vanilla environments are supported. In vanilla, a `d3` global is exported:
29
30 ```html
31 <script src="https://d3js.org/d3-path.v1.min.js"></script>
32 <script src="https://d3js.org/d3-shape.v1.min.js"></script>
33 <script>
34
35 var line = d3.line();
36
37 </script>
38 ```
39
40 [Try d3-shape in your browser.](https://tonicdev.com/npm/d3-shape)
41
42 ## API Reference
43
44 * [Arcs](#arcs)
45 * [Pies](#pies)
46 * [Lines](#lines)
47 * [Areas](#areas)
48 * [Curves](#curves)
49 * [Custom Curves](#custom-curves)
50 * [Links](#links)
51 * [Symbols](#symbols)
52 * [Custom Symbol Types](#custom-symbol-types)
53 * [Stacks](#stacks)
54
55 ### Arcs
56
57 [<img alt="Pie Chart" src="https://raw.githubusercontent.com/d3/d3-shape/master/img/pie.png" width="295" height="295">](http://bl.ocks.org/mbostock/8878e7fd82034f1d63cf)[<img alt="Donut Chart" src="https://raw.githubusercontent.com/d3/d3-shape/master/img/donut.png" width="295" height="295">](http://bl.ocks.org/mbostock/2394b23da1994fc202e1)
58
59 The arc generator produces a [circular](https://en.wikipedia.org/wiki/Circular_sector) or [annular](https://en.wikipedia.org/wiki/Annulus_\(mathematics\)) sector, as in a pie or donut chart. If the difference between the [start](#arc_startAngle) and [end](#arc_endAngle) angles (the *angular span*) is greater than [τ](https://en.wikipedia.org/wiki/Turn_\(geometry\)#Tau_proposal), the arc generator will produce a complete circle or annulus. If it is less than τ, arcs may have [rounded corners](#arc_cornerRadius) and [angular padding](#arc_padAngle). Arcs are always centered at ⟨0,0⟩; use a transform (see: [SVG](http://www.w3.org/TR/SVG/coords.html#TransformAttribute), [Canvas](http://www.w3.org/TR/2dcontext/#transformations)) to move the arc to a different position.
60
61 See also the [pie generator](#pies), 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.
62
63 <a name="arc" href="#arc">#</a> d3.<b>arc</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/arc.js "Source")
64
65 Constructs a new arc generator with the default settings.
66
67 <a name="_arc" href="#_arc">#</a> <i>arc</i>(<i>arguments…</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/arc.js#L89 "Source")
68
69 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:
70
71 ```js
72 var arc = d3.arc();
73
74 arc({
75 innerRadius: 0,
76 outerRadius: 100,
77 startAngle: 0,
78 endAngle: Math.PI / 2
79 }); // "M0,-100A100,100,0,0,1,100,0L0,0Z"
80 ```
81
82 If the radii and angles are instead defined as constants, you can generate an arc without any arguments:
83
84 ```js
85 var arc = d3.arc()
86 .innerRadius(0)
87 .outerRadius(100)
88 .startAngle(0)
89 .endAngle(Math.PI / 2);
90
91 arc(); // "M0,-100A100,100,0,0,1,100,0L0,0Z"
92 ```
93
94 If the arc generator has a [context](#arc_context), then the arc is rendered to this context as a sequence of [path method](http://www.w3.org/TR/2dcontext/#canvaspathmethods) calls and this function returns void. Otherwise, a [path data](http://www.w3.org/TR/SVG/paths.html#PathData) string is returned.
95
96 <a name="arc_centroid" href="#arc_centroid">#</a> <i>arc</i>.<b>centroid</b>(<i>arguments…</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/arc.js#L224 "Source")
97
98 Computes the midpoint [*x*, *y*] of the center line of the arc that would be [generated](#_arc) 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](#arc_startAngle) + [endAngle](#arc_endAngle)) / 2 and ([innerRadius](#arc_innerRadius) + [outerRadius](#arc_outerRadius)) / 2. For example:
99
100 [<img alt="Circular Sector Centroids" src="https://raw.githubusercontent.com/d3/d3-shape/master/img/centroid-circular-sector.png" width="250" height="250">](http://bl.ocks.org/mbostock/9b5a2fd1ce1a146f27e4)[<img alt="Annular Sector Centroids" src="https://raw.githubusercontent.com/d3/d3-shape/master/img/centroid-annular-sector.png" width="250" height="250">](http://bl.ocks.org/mbostock/c274877f647361f3df7d)
101
102 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.
103
104 <a name="arc_innerRadius" href="#arc_innerRadius">#</a> <i>arc</i>.<b>innerRadius</b>([<i>radius</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/arc.js#L230 "Source")
105
106 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:
107
108 ```js
109 function innerRadius(d) {
110 return d.innerRadius;
111 }
112 ```
113
114 Specifying the inner radius as a function is useful for constructing a stacked polar bar chart, often in conjunction with a [sqrt scale](https://github.com/d3/d3-scale#sqrt). 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.
115
116 <a name="arc_outerRadius" href="#arc_outerRadius">#</a> <i>arc</i>.<b>outerRadius</b>([<i>radius</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/arc.js#L234 "Source")
117
118 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:
119
120 ```js
121 function outerRadius(d) {
122 return d.outerRadius;
123 }
124 ```
125
126 Specifying the outer radius as a function is useful for constructing a coxcomb or polar bar chart, often in conjunction with a [sqrt scale](https://github.com/d3/d3-scale#sqrt). 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.
127
128 <a name="arc_cornerRadius" href="#arc_cornerRadius">#</a> <i>arc</i>.<b>cornerRadius</b>([<i>radius</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/arc.js#L238 "Source")
129
130 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:
131
132 ```js
133 function cornerRadius() {
134 return 0;
135 }
136 ```
137
138 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:
139
140 [<img alt="Rounded Circular Sectors" src="https://raw.githubusercontent.com/d3/d3-shape/master/img/rounded-circular-sector.png" width="250" height="250">](http://bl.ocks.org/mbostock/e5e3680f3079cf5c3437)[<img alt="Rounded Annular Sectors" src="https://raw.githubusercontent.com/d3/d3-shape/master/img/rounded-annular-sector.png" width="250" height="250">](http://bl.ocks.org/mbostock/f41f50e06a6c04828b6e)
141
142 The corner radius may not be larger than ([outerRadius](#arc_outerRadius) - [innerRadius](#arc_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](http://bl.ocks.org/mbostock/b7671cb38efdfa5da3af) for illustration.
143
144 <a name="arc_startAngle" href="#arc_startAngle">#</a> <i>arc</i>.<b>startAngle</b>([<i>angle</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/arc.js#L246 "Source")
145
146 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:
147
148 ```js
149 function startAngle(d) {
150 return d.startAngle;
151 }
152 ```
153
154 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.
155
156 <a name="arc_endAngle" href="#arc_endAngle">#</a> <i>arc</i>.<b>endAngle</b>([<i>angle</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/arc.js#L250 "Source")
157
158 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:
159
160 ```js
161 function endAngle(d) {
162 return d.endAngle;
163 }
164 ```
165
166 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.
167
168 <a name="arc_padAngle" href="#arc_padAngle">#</a> <i>arc</i>.<b>padAngle</b>([<i>angle</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/arc.js#L254 "Source")
169
170 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:
171
172 ```js
173 function padAngle() {
174 return d && d.padAngle;
175 }
176 ```
177
178 The pad angle is converted to a fixed linear distance separating adjacent arcs, defined as [padRadius](#arc_padRadius) * padAngle. This distance is subtracted equally from the [start](#arc_startAngle) and [end](#arc_endAngle) of the arc. If the arc forms a complete circle or annulus, as when |endAngle - startAngle| ≥ τ, the pad angle is ignored.
179
180 If the [inner radius](#arc_innerRadius) 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:
181
182 [<img alt="Padded Circular Sectors" src="https://raw.githubusercontent.com/d3/d3-shape/master/img/padded-circular-sector.png" width="250" height="250">](http://bl.ocks.org/mbostock/f37b07b92633781a46f7)[<img alt="Padded Annular Sectors" src="https://raw.githubusercontent.com/d3/d3-shape/master/img/padded-annular-sector.png" width="250" height="250">](http://bl.ocks.org/mbostock/99f0a6533f7c949cf8b8)
183
184 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](http://bl.ocks.org/mbostock/053fcc2295a445afab07) for illustration.
185
186 Often, the pad angle is not set directly on the arc generator, but is instead computed by the [pie generator](#pies) so as to ensure that the area of padded arcs is proportional to their value; see [*pie*.padAngle](#pie_padAngle). See the [pie padding animation](http://bl.ocks.org/mbostock/3e961b4c97a1b543fff2) for illustration. If you apply a constant pad angle to the arc generator directly, it tends to subtract disproportionately from smaller arcs, introducing distortion.
187
188 <a name="arc_padRadius" href="#arc_padRadius">#</a> <i>arc</i>.<b>padRadius</b>([<i>radius</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/arc.js#L242 "Source")
189
190 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](#arc_innerRadius) * innerRadius + [outerRadius](#arc_outerRadius) * outerRadius). The pad radius determines the fixed linear distance separating adjacent arcs, defined as padRadius * [padAngle](#arc_padAngle).
191
192 <a name="arc_context" href="#arc_context">#</a> <i>arc</i>.<b>context</b>([<i>context</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/arc.js#L258 "Source")
193
194 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](#_arc) is rendered to this context as a sequence of [path method](http://www.w3.org/TR/2dcontext/#canvaspathmethods) calls. Otherwise, a [path data](http://www.w3.org/TR/SVG/paths.html#PathData) string representing the generated arc is returned.
195
196 ### Pies
197
198 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](#arcs).
199
200 <a name="pie" href="#pie">#</a> d3.<b>pie</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/pie.js "Source")
201
202 Constructs a new pie generator with the default settings.
203
204 <a name="_pie" href="#_pie">#</a> <i>pie</i>(<i>data</i>[, <i>arguments…</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/pie.js#L14 "Source")
205
206 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:
207
208 * `data` - the input datum; the corresponding element in the input data array.
209 * `value` - the numeric [value](#pie_value) of the arc.
210 * `index` - the zero-based [sorted index](#pie_sort) of the arc.
211 * `startAngle` - the [start angle](#pie_startAngle) of the arc.
212 * `endAngle` - the [end angle](#pie_endAngle) of the arc.
213 * `padAngle` - the [pad angle](#pie_padAngle) of the arc.
214
215 This representation is designed to work with the arc generator’s default [startAngle](#arc_startAngle), [endAngle](#arc_endAngle) and [padAngle](#arc_padAngle) accessors. The angular units are arbitrary, but if you plan to use the pie generator in conjunction with an [arc generator](#arcs), you should specify angles in radians, with 0 at -*y* (12 o’clock) and positive angles proceeding clockwise.
216
217 Given a small dataset of numbers, here is how to compute the arc angles to render this data as a pie chart:
218
219 ```js
220 var data = [1, 1, 2, 3, 5, 8, 13, 21];
221 var arcs = d3.pie()(data);
222 ```
223
224 The first pair of parens, `pie()`, [constructs](#pie) a default pie generator. The second, `pie()(data)`, [invokes](#_pie) this generator on the dataset, returning an array of objects:
225
226 ```json
227 [
228 {"data": 1, "value": 1, "index": 6, "startAngle": 6.050474740247008, "endAngle": 6.166830023713296, "padAngle": 0},
229 {"data": 1, "value": 1, "index": 7, "startAngle": 6.166830023713296, "endAngle": 6.283185307179584, "padAngle": 0},
230 {"data": 2, "value": 2, "index": 5, "startAngle": 5.817764173314431, "endAngle": 6.050474740247008, "padAngle": 0},
231 {"data": 3, "value": 3, "index": 4, "startAngle": 5.468698322915565, "endAngle": 5.817764173314431, "padAngle": 0},
232 {"data": 5, "value": 5, "index": 3, "startAngle": 4.886921905584122, "endAngle": 5.468698322915565, "padAngle": 0},
233 {"data": 8, "value": 8, "index": 2, "startAngle": 3.956079637853813, "endAngle": 4.886921905584122, "padAngle": 0},
234 {"data": 13, "value": 13, "index": 1, "startAngle": 2.443460952792061, "endAngle": 3.956079637853813, "padAngle": 0},
235 {"data": 21, "value": 21, "index": 0, "startAngle": 0.000000000000000, "endAngle": 2.443460952792061, "padAngle": 0}
236 ]
237 ```
238
239 Note that the returned array is in the same order as the data, even though this pie chart is [sorted](#pie_sortValues) by descending value, starting with the arc for the last datum (value 21) at 12 o’clock.
240
241 <a name="pie_value" href="#pie_value">#</a> <i>pie</i>.<b>value</b>([<i>value</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/pie.js#L54 "Source")
242
243 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:
244
245 ```js
246 function value(d) {
247 return d;
248 }
249 ```
250
251 When a pie is [generated](#_pie), 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/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:
252
253 ```js
254 var data = [
255 {"number": 4, "name": "Locke"},
256 {"number": 8, "name": "Reyes"},
257 {"number": 15, "name": "Ford"},
258 {"number": 16, "name": "Jarrah"},
259 {"number": 23, "name": "Shephard"},
260 {"number": 42, "name": "Kwon"}
261 ];
262
263 var arcs = d3.pie()
264 .value(function(d) { return d.number; })
265 (data);
266 ```
267
268 This is similar to [mapping](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) your data to values before invoking the pie generator:
269
270 ```js
271 var arcs = d3.pie()(data.map(function(d) { return d.number; }));
272 ```
273
274 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.
275
276 <a name="pie_sort" href="#pie_sort">#</a> <i>pie</i>.<b>sort</b>([<i>compare</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/pie.js#L62 "Source")
277
278 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](#pie_sortValues) to null.
279
280 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:
281
282 ```js
283 pie.sort(function(a, b) { return a.name.localeCompare(b.name); });
284 ```
285
286 Sorting does not affect the order of the [generated arc array](#_pie) 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](#pie_startAngle) and the last arc ends at the [end angle](#pie_endAngle).
287
288 <a name="pie_sortValues" href="#pie_sortValues">#</a> <i>pie</i>.<b>sortValues</b>([<i>compare</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/pie.js#L58 "Source")
289
290 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:
291
292 ```js
293 function compare(a, b) {
294 return b - a;
295 }
296 ```
297
298 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](#pie_sort) to null.
299
300 The value comparator is similar to the [data comparator](#pie_sort), except the two arguments *a* and *b* are values derived from the input data array using the [value accessor](#pie_value), 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:
301
302 ```js
303 pie.sortValues(function(a, b) { return a - b; });
304 ```
305
306 Sorting does not affect the order of the [generated arc array](#_pie) 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](#pie_startAngle) and the last arc ends at the [end angle](#pie_endAngle).
307
308 <a name="pie_startAngle" href="#pie_startAngle">#</a> <i>pie</i>.<b>startAngle</b>([<i>angle</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/pie.js#L66 "Source")
309
310 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:
311
312 ```js
313 function startAngle() {
314 return 0;
315 }
316 ```
317
318 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](#_pie). The units of *angle* are arbitrary, but if you plan to use the pie generator in conjunction with an [arc generator](#arcs), you should specify an angle in radians, with 0 at -*y* (12 o’clock) and positive angles proceeding clockwise.
319
320 <a name="pie_endAngle" href="#pie_endAngle">#</a> <i>pie</i>.<b>endAngle</b>([<i>angle</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/pie.js#L70 "Source")
321
322 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:
323
324 ```js
325 function endAngle() {
326 return 2 * Math.PI;
327 }
328 ```
329
330 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](#_pie). The units of *angle* are arbitrary, but if you plan to use the pie generator in conjunction with an [arc generator](#arcs), you should specify an angle in radians, with 0 at -*y* (12 o’clock) and positive angles proceeding clockwise.
331
332 The value of the end angle is constrained to [startAngle](#pie_startAngle) ± τ, such that |endAngle - startAngle| ≤ τ.
333
334 <a name="pie_padAngle" href="#pie_padAngle">#</a> <i>pie</i>.<b>padAngle</b>([<i>angle</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/pie.js#L74 "Source")
335
336 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:
337
338 ```js
339 function padAngle() {
340 return 0;
341 }
342 ```
343
344 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](#pie_value) such that the relative area of each arc is preserved. See the [pie padding animation](http://bl.ocks.org/mbostock/3e961b4c97a1b543fff2) for illustration. The pad angle accessor is invoked once, being passed the same arguments and `this` context as the [pie generator](#_pie). The units of *angle* are arbitrary, but if you plan to use the pie generator in conjunction with an [arc generator](#arcs), you should specify an angle in radians.
345
346 ### Lines
347
348 [<img width="295" height="154" alt="Line Chart" src="https://raw.githubusercontent.com/d3/d3-shape/master/img/line.png">](http://bl.ocks.org/mbostock/1550e57e12e73b86ad9e)
349
350 The line generator produces a [spline](https://en.wikipedia.org/wiki/Spline_\(mathematics\)) or [polyline](https://en.wikipedia.org/wiki/Polygonal_chain), as in a line chart. Lines also appear in many other visualization types, such as the links in [hierarchical edge bundling](http://bl.ocks.org/mbostock/7607999).
351
352 <a name="line" href="#line">#</a> d3.<b>line</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/line.js "Source")
353
354 Constructs a new line generator with the default settings.
355
356 <a name="_line" href="#_line">#</a> <i>line</i>(<i>data</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/line.js#L14 "Source")
357
358 Generates a line for the given array of *data*. Depending on this line generator’s associated [curve](#line_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](#line_context), then the line is rendered to this context as a sequence of [path method](http://www.w3.org/TR/2dcontext/#canvaspathmethods) calls and this function returns void. Otherwise, a [path data](http://www.w3.org/TR/SVG/paths.html#PathData) string is returned.
359
360 <a name="line_x" href="#line_x">#</a> <i>line</i>.<b>x</b>([<i>x</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/line.js#L34 "Source")
361
362 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:
363
364 ```js
365 function x(d) {
366 return d[0];
367 }
368 ```
369
370 When a line is [generated](#_line), the x accessor will be invoked for each [defined](#line_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](https://github.com/d3/d3-scale#time-scales) and `y` is a [linear scale](https://github.com/d3/d3-scale#linear-scales):
371
372 ```js
373 var data = [
374 {date: new Date(2007, 3, 24), value: 93.24},
375 {date: new Date(2007, 3, 25), value: 95.35},
376 {date: new Date(2007, 3, 26), value: 98.84},
377 {date: new Date(2007, 3, 27), value: 99.92},
378 {date: new Date(2007, 3, 30), value: 99.80},
379 {date: new Date(2007, 4, 1), value: 99.47},
380
381 ];
382
383 var line = d3.line()
384 .x(function(d) { return x(d.date); })
385 .y(function(d) { return y(d.value); });
386 ```
387
388 <a name="line_y" href="#line_y">#</a> <i>line</i>.<b>y</b>([<i>y</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/line.js#L38 "Source")
389
390 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:
391
392 ```js
393 function y(d) {
394 return d[1];
395 }
396 ```
397
398 When a line is [generated](#_line), the y accessor will be invoked for each [defined](#line_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](#line_x) for more information.
399
400 <a name="line_defined" href="#line_defined">#</a> <i>line</i>.<b>defined</b>([<i>defined</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/line.js#L42 "Source")
401
402 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:
403
404 ```js
405 function defined() {
406 return true;
407 }
408 ```
409
410 The default accessor thus assumes that the input data is always defined. When a line is [generated](#_line), 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](#line_x) and [y](#line_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:
411
412 [<img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/line-defined.png" width="480" height="250" alt="Line with Missing Data">](http://bl.ocks.org/mbostock/0533f44f2cfabecc5e3a)
413
414 Note that if a line segment consists of only a single point, it may appear invisible unless rendered with rounded or square [line caps](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-linecap). In addition, some curves such as [curveCardinalOpen](#curveCardinalOpen) only render a visible segment if it contains multiple points.
415
416 <a name="line_curve" href="#line_curve">#</a> <i>line</i>.<b>curve</b>([<i>curve</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/line.js#L46 "Source")
417
418 If *curve* is specified, sets the [curve factory](#curves) and returns this line generator. If *curve* is not specified, returns the current curve factory, which defaults to [curveLinear](#curveLinear).
419
420 <a name="line_context" href="#line_context">#</a> <i>line</i>.<b>context</b>([<i>context</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/line.js#L50 "Source")
421
422 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](#_line) is rendered to this context as a sequence of [path method](http://www.w3.org/TR/2dcontext/#canvaspathmethods) calls. Otherwise, a [path data](http://www.w3.org/TR/SVG/paths.html#PathData) string representing the generated line is returned.
423
424 <a name="lineRadial" href="#lineRadial">#</a> d3.<b>lineRadial</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/lineRadial.js "Source")
425
426 <img alt="Radial Line" width="250" height="250" src="https://raw.githubusercontent.com/d3/d3-shape/master/img/line-radial.png">
427
428 Constructs a new radial line generator with the default settings. A radial line generator is equivalent to the standard Cartesian [line generator](#line), except the [x](#line_x) and [y](#line_y) accessors are replaced with [angle](#lineRadial_angle) and [radius](#lineRadial_radius) accessors. Radial lines are always positioned relative to ⟨0,0⟩; use a transform (see: [SVG](http://www.w3.org/TR/SVG/coords.html#TransformAttribute), [Canvas](http://www.w3.org/TR/2dcontext/#transformations)) to change the origin.
429
430 <a name="_lineRadial" href="#_lineRadial">#</a> <i>lineRadial</i>(<i>data</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/lineRadial.js#L4 "Source")
431
432 Equivalent to [*line*](#_line).
433
434 <a name="lineRadial_angle" href="#lineRadial_angle">#</a> <i>lineRadial</i>.<b>angle</b>([<i>angle</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/lineRadial.js#L7 "Source")
435
436 Equivalent to [*line*.x](#line_x), except the accessor returns the angle in radians, with 0 at -*y* (12 o’clock).
437
438 <a name="lineRadial_radius" href="#lineRadial_radius">#</a> <i>lineRadial</i>.<b>radius</b>([<i>radius</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/lineRadial.js#L8 "Source")
439
440 Equivalent to [*line*.y](#line_y), except the accessor returns the radius: the distance from the origin ⟨0,0⟩.
441
442 <a name="lineRadial_defined" href="#lineRadial_defined">#</a> <i>lineRadial</i>.<b>defined</b>([<i>defined</i>])
443
444 Equivalent to [*line*.defined](#line_defined).
445
446 <a name="lineRadial_curve" href="#lineRadial_curve">#</a> <i>lineRadial</i>.<b>curve</b>([<i>curve</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/lineRadial.js#L10 "Source")
447
448 Equivalent to [*line*.curve](#line_curve). Note that [curveMonotoneX](#curveMonotoneX) or [curveMonotoneY](#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.
449
450 <a name="lineRadial_context" href="#lineRadial_context">#</a> <i>lineRadial</i>.<b>context</b>([<i>context</i>])
451
452 Equivalent to [*line*.context](#line_context).
453
454 ### Areas
455
456 [<img alt="Area Chart" width="295" height="154" src="https://raw.githubusercontent.com/d3/d3-shape/master/img/area.png">](http://bl.ocks.org/mbostock/3883195)[<img alt="Stacked Area Chart" width="295" height="154" src="https://raw.githubusercontent.com/d3/d3-shape/master/img/area-stacked.png">](http://bl.ocks.org/mbostock/3885211)[<img alt="Difference Chart" width="295" height="154" src="https://raw.githubusercontent.com/d3/d3-shape/master/img/area-difference.png">](http://bl.ocks.org/mbostock/3894205)
457
458 The area generator produces an area, as in an area chart. An area is defined by two bounding [lines](#lines), either splines or polylines. Typically, the two lines share the same [*x*-values](#area_x) ([x0](#area_x0) = [x1](#area_x1)), differing only in *y*-value ([y0](#area_y0) and [y1](#area_y1)); most commonly, y0 is defined as a constant representing [zero](http://www.vox.com/2015/11/19/9758062/y-axis-zero-chart). The first line (the <i>topline</i>) is defined by x1 and y1 and is rendered first; the second line (the <i>baseline</i>) is defined by x0 and y0 and is rendered second, with the points in reverse order. With a [curveLinear](#curveLinear) [curve](#area_curve), this produces a clockwise polygon.
459
460 <a name="area" href="#area">#</a> d3.<b>area</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/area.js "Source")
461
462 Constructs a new area generator with the default settings.
463
464 <a name="_area" href="#_area">#</a> <i>area</i>(<i>data</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/area.js#L17 "Source")
465
466 Generates an area for the given array of *data*. Depending on this area generator’s associated [curve](#area_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](#line_context), then the area is rendered to this context as a sequence of [path method](http://www.w3.org/TR/2dcontext/#canvaspathmethods) calls and this function returns void. Otherwise, a [path data](http://www.w3.org/TR/SVG/paths.html#PathData) string is returned.
467
468 <a name="area_x" href="#area_x">#</a> <i>area</i>.<b>x</b>([<i>x</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/area.js#L59 "Source")
469
470 If *x* is specified, sets [x0](#area_x0) to *x* and [x1](#area_x1) to null and returns this area generator. If *x* is not specified, returns the current x0 accessor.
471
472 <a name="area_x0" href="#area_x0">#</a> <i>area</i>.<b>x0</b>([<i>x</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/area.js#L63 "Source")
473
474 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:
475
476 ```js
477 function x(d) {
478 return d[0];
479 }
480 ```
481
482 When an area is [generated](#_area), the x0 accessor will be invoked for each [defined](#area_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](https://github.com/d3/d3-scale#time-scales) and `y` is a [linear scale](https://github.com/d3/d3-scale#linear-scales):
483
484 ```js
485 var data = [
486 {date: new Date(2007, 3, 24), value: 93.24},
487 {date: new Date(2007, 3, 25), value: 95.35},
488 {date: new Date(2007, 3, 26), value: 98.84},
489 {date: new Date(2007, 3, 27), value: 99.92},
490 {date: new Date(2007, 3, 30), value: 99.80},
491 {date: new Date(2007, 4, 1), value: 99.47},
492
493 ];
494
495 var area = d3.area()
496 .x(function(d) { return x(d.date); })
497 .y1(function(d) { return y(d.value); })
498 .y0(y(0));
499 ```
500
501 <a name="area_x1" href="#area_x1">#</a> <i>area</i>.<b>x1</b>([<i>x</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/area.js#L67 "Source")
502
503 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](#area_x0) value should be reused for the x1 value.
504
505 When an area is [generated](#_area), the x1 accessor will be invoked for each [defined](#area_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](#area_x0) for more information.
506
507 <a name="area_y" href="#area_y">#</a> <i>area</i>.<b>y</b>([<i>y</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/area.js#L71 "Source")
508
509 If *y* is specified, sets [y0](#area_y0) to *y* and [y1](#area_y1) to null and returns this area generator. If *y* is not specified, returns the current y0 accessor.
510
511 <a name="area_y0" href="#area_y0">#</a> <i>area</i>.<b>y0</b>([<i>y</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/area.js#L75 "Source")
512
513 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:
514
515 ```js
516 function y() {
517 return 0;
518 }
519 ```
520
521 When an area is [generated](#_area), the y0 accessor will be invoked for each [defined](#area_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](#area_x0) for more information.
522
523 <a name="area_y1" href="#area_y1">#</a> <i>area</i>.<b>y1</b>([<i>y</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/area.js#L79 "Source")
524
525 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:
526
527 ```js
528 function y(d) {
529 return d[1];
530 }
531 ```
532
533 A null accessor is also allowed, indicating that the previously-computed [y0](#area_y0) value should be reused for the y1 value. When an area is [generated](#_area), the y1 accessor will be invoked for each [defined](#area_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](#area_x0) for more information.
534
535 <a name="area_defined" href="#area_defined">#</a> <i>area</i>.<b>defined</b>([<i>defined</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/area.js#L96 "Source")
536
537 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:
538
539 ```js
540 function defined() {
541 return true;
542 }
543 ```
544
545 The default accessor thus assumes that the input data is always defined. When an area is [generated](#_area), 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](#area_x0), [x1](#area_x1), [y0](#area_y0) and [y1](#area_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:
546
547 [<img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/area-defined.png" width="480" height="250" alt="Area with Missing Data">](http://bl.ocks.org/mbostock/3035090)
548
549 Note that if an area segment consists of only a single point, it may appear invisible unless rendered with rounded or square [line caps](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-linecap). In addition, some curves such as [curveCardinalOpen](#curveCardinalOpen) only render a visible segment if it contains multiple points.
550
551 <a name="area_curve" href="#area_curve">#</a> <i>area</i>.<b>curve</b>([<i>curve</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/area.js#L100 "Source")
552
553 If *curve* is specified, sets the [curve factory](#curves) and returns this area generator. If *curve* is not specified, returns the current curve factory, which defaults to [curveLinear](#curveLinear).
554
555 <a name="area_context" href="#area_context">#</a> <i>area</i>.<b>context</b>([<i>context</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/area.js#L104 "Source")
556
557 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](#_area) is rendered to this context as a sequence of [path method](http://www.w3.org/TR/2dcontext/#canvaspathmethods) calls. Otherwise, a [path data](http://www.w3.org/TR/SVG/paths.html#PathData) string representing the generated area is returned.
558
559 <a name="area_lineX0" href="#area_lineX0">#</a> <i>area</i>.<b>lineX0</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/area.js#L83 "Source")
560 <br><a name="area_lineY0" href="#area_lineY0">#</a> <i>area</i>.<b>lineY0</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/area.js#L84 "Source")
561
562 Returns a new [line generator](#lines) that has this area generator’s current [defined accessor](#area_defined), [curve](#area_curve) and [context](#area_context). The line’s [*x*-accessor](#line_x) is this area’s [*x0*-accessor](#area_x0), and the line’s [*y*-accessor](#line_y) is this area’s [*y0*-accessor](#area_y0).
563
564 <a name="area_lineX1" href="#area_lineX1">#</a> <i>area</i>.<b>lineX1</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/area.js#L92 "Source")
565
566 Returns a new [line generator](#lines) that has this area generator’s current [defined accessor](#area_defined), [curve](#area_curve) and [context](#area_context). The line’s [*x*-accessor](#line_x) is this area’s [*x1*-accessor](#area_x1), and the line’s [*y*-accessor](#line_y) is this area’s [*y0*-accessor](#area_y0).
567
568 <a name="area_lineY1" href="#area_lineY1">#</a> <i>area</i>.<b>lineY1</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/area.js#L88 "Source")
569
570 Returns a new [line generator](#lines) that has this area generator’s current [defined accessor](#area_defined), [curve](#area_curve) and [context](#area_context). The line’s [*x*-accessor](#line_x) is this area’s [*x0*-accessor](#area_x0), and the line’s [*y*-accessor](#line_y) is this area’s [*y1*-accessor](#area_y1).
571
572 <a name="areaRadial" href="#areaRadial">#</a> d3.<b>areaRadial</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/areaRadial.js "Source")
573
574 <img alt="Radial Area" width="250" height="250" src="https://raw.githubusercontent.com/d3/d3-shape/master/img/area-radial.png">
575
576 Constructs a new radial area generator with the default settings. A radial area generator is equivalent to the standard Cartesian [area generator](#area), except the [x](#area_x) and [y](#area_y) accessors are replaced with [angle](#areaRadial_angle) and [radius](#areaRadial_radius) accessors. Radial areas are always positioned relative to ⟨0,0⟩; use a transform (see: [SVG](http://www.w3.org/TR/SVG/coords.html#TransformAttribute), [Canvas](http://www.w3.org/TR/2dcontext/#transformations)) to change the origin.
577
578 <a name="_areaRadial" href="#_areaRadial">#</a> <i>areaRadial</i>(<i>data</i>)
579
580 Equivalent to [*area*](#_area).
581
582 <a name="areaRadial_angle" href="#areaRadial_angle">#</a> <i>areaRadial</i>.<b>angle</b>([<i>angle</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/areaRadial.js#L13 "Source")
583
584 Equivalent to [*area*.x](#area_x), except the accessor returns the angle in radians, with 0 at -*y* (12 o’clock).
585
586 <a name="areaRadial_startAngle" href="#areaRadial_startAngle">#</a> <i>areaRadial</i>.<b>startAngle</b>([<i>angle</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/areaRadial.js#L14 "Source")
587
588 Equivalent to [*area*.x0](#area_x0), except the accessor returns the angle in radians, with 0 at -*y* (12 o’clock). Note: typically [angle](#areaRadial_angle) is used instead of setting separate start and end angles.
589
590 <a name="areaRadial_endAngle" href="#areaRadial_endAngle">#</a> <i>areaRadial</i>.<b>endAngle</b>([<i>angle</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/areaRadial.js#L15 "Source")
591
592 Equivalent to [*area*.x1](#area_x1), except the accessor returns the angle in radians, with 0 at -*y* (12 o’clock). Note: typically [angle](#areaRadial_angle) is used instead of setting separate start and end angles.
593
594 <a name="areaRadial_radius" href="#areaRadial_radius">#</a> <i>areaRadial</i>.<b>radius</b>([<i>radius</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/areaRadial.js#L16 "Source")
595
596 Equivalent to [*area*.y](#area_y), except the accessor returns the radius: the distance from the origin ⟨0,0⟩.
597
598 <a name="areaRadial_innerRadius" href="#areaRadial_innerRadius">#</a> <i>areaRadial</i>.<b>innerRadius</b>([<i>radius</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/areaRadial.js#L17 "Source")
599
600 Equivalent to [*area*.y0](#area_y0), except the accessor returns the radius: the distance from the origin ⟨0,0⟩.
601
602 <a name="areaRadial_outerRadius" href="#areaRadial_outerRadius">#</a> <i>areaRadial</i>.<b>outerRadius</b>([<i>radius</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/areaRadial.js#L18 "Source")
603
604 Equivalent to [*area*.y1](#area_y1), except the accessor returns the radius: the distance from the origin ⟨0,0⟩.
605
606 <a name="areaRadial_defined" href="#areaRadial_defined">#</a> <i>areaRadial</i>.<b>defined</b>([<i>defined</i>])
607
608 Equivalent to [*area*.defined](#area_defined).
609
610 <a name="areaRadial_curve" href="#areaRadial_curve">#</a> <i>areaRadial</i>.<b>curve</b>([<i>curve</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/areaRadial.js#L24 "Source")
611
612 Equivalent to [*area*.curve](#area_curve). Note that [curveMonotoneX](#curveMonotoneX) or [curveMonotoneY](#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.
613
614 <a name="areaRadial_context" href="#areaRadial_context">#</a> <i>areaRadial</i>.<b>context</b>([<i>context</i>])
615
616 Equivalent to [*line*.context](#line_context).
617
618 <a name="areaRadial_lineStartAngle" href="#areaRadial_lineStartAngle">#</a> <i>areaRadial</i>.<b>lineStartAngle</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/areaRadial.js#L19 "Source")
619 <br><a name="areaRadial_lineInnerRadius" href="#areaRadial_lineInnerRadius">#</a> <i>areaRadial</i>.<b>lineInnerRadius</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/areaRadial.js#L21 "Source")
620
621 Returns a new [radial line generator](#lineRadial) that has this radial area generator’s current [defined accessor](#areaRadial_defined), [curve](#areaRadial_curve) and [context](#areaRadial_context). The line’s [angle accessor](#lineRadial_angle) is this area’s [start angle accessor](#areaRadial_startAngle), and the line’s [radius accessor](#lineRadial_radius) is this area’s [inner radius accessor](#areaRadial_innerRadius).
622
623 <a name="areaRadial_lineEndAngle" href="#areaRadial_lineEndAngle">#</a> <i>areaRadial</i>.<b>lineEndAngle</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/areaRadial.js#L20 "Source")
624
625 Returns a new [radial line generator](#lineRadial) that has this radial area generator’s current [defined accessor](#areaRadial_defined), [curve](#areaRadial_curve) and [context](#areaRadial_context). The line’s [angle accessor](#lineRadial_angle) is this area’s [end angle accessor](#areaRadial_endAngle), and the line’s [radius accessor](#lineRadial_radius) is this area’s [inner radius accessor](#areaRadial_innerRadius).
626
627 <a name="areaRadial_lineOuterRadius" href="#areaRadial_lineOuterRadius">#</a> <i>areaRadial</i>.<b>lineOuterRadius</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/areaRadial.js#L22 "Source")
628
629 Returns a new [radial line generator](#lineRadial) that has this radial area generator’s current [defined accessor](#areaRadial_defined), [curve](#areaRadial_curve) and [context](#areaRadial_context). The line’s [angle accessor](#lineRadial_angle) is this area’s [start angle accessor](#areaRadial_startAngle), and the line’s [radius accessor](#lineRadial_radius) is this area’s [outer radius accessor](#areaRadial_outerRadius).
630
631 ### Curves
632
633 While [lines](#lines) are defined as a sequence of two-dimensional [*x*, *y*] points, and [areas](#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.
634
635 Curves are typically not constructed or used directly, instead being passed to [*line*.curve](#line_curve) and [*area*.curve](#area_curve). For example:
636
637 ```js
638 var line = d3.line()
639 .x(function(d) { return x(d.date); })
640 .y(function(d) { return y(d.value); })
641 .curve(d3.curveCatmullRom.alpha(0.5));
642 ```
643
644 <a name="curveBasis" href="#curveBasis">#</a> d3.<b>curveBasis</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/basis.js#L12 "Source")
645
646 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/basis.png" width="888" height="240" alt="basis">
647
648 Produces a cubic [basis spline](https://en.wikipedia.org/wiki/B-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.
649
650 <a name="curveBasisClosed" href="#curveBasisClosed">#</a> d3.<b>curveBasisClosed</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/basisClosed.js "Source")
651
652 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/basisClosed.png" width="888" height="240" alt="basisClosed">
653
654 Produces a closed cubic [basis spline](https://en.wikipedia.org/wiki/B-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.
655
656 <a name="curveBasisOpen" href="#curveBasisOpen">#</a> d3.<b>curveBasisOpen</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/basisOpen.js "Source")
657
658 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/basisOpen.png" width="888" height="240" alt="basisOpen">
659
660 Produces a cubic [basis spline](https://en.wikipedia.org/wiki/B-spline) using the specified control points. Unlike [basis](#basis), the first and last points are not repeated, and thus the curve typically does not intersect these points.
661
662 <a name="curveBundle" href="#curveBundle">#</a> d3.<b>curveBundle</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/bundle.js "Source")
663
664 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/bundle.png" width="888" height="240" alt="bundle">
665
666 Produces a straightened cubic [basis spline](https://en.wikipedia.org/wiki/B-spline) using the specified control points, with the spline straightened according to the curve’s [*beta*](#curveBundle_beta), which defaults to 0.85. This curve is typically used in [hierarchical edge bundling](http://bl.ocks.org/mbostock/7607999) to disambiguate connections, as proposed by [Danny Holten](https://www.win.tue.nl/vis1/home/dholten/) in [Hierarchical Edge Bundles: Visualization of Adjacency Relations in Hierarchical Data](https://www.win.tue.nl/vis1/home/dholten/papers/bundles_infovis.pdf). This curve does not implement [*curve*.areaStart](#curve_areaStart) and [*curve*.areaEnd](#curve_areaEnd); it is intended to work with [d3.line](#lines), not [d3.area](#areas).
667
668 <a name="curveBundle_beta" href="#curveBundle_beta">#</a> <i>bundle</i>.<b>beta</b>(<i>beta</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/bundle.js#L51 "Source")
669
670 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](#basis) spline is produced. For example:
671
672 ```js
673 var line = d3.line().curve(d3.curveBundle.beta(0.5));
674 ```
675
676 <a name="curveCardinal" href="#curveCardinal">#</a> d3.<b>curveCardinal</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/cardinal.js "Source")
677
678 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/cardinal.png" width="888" height="240" alt="cardinal">
679
680 Produces a cubic [cardinal spline](https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline) using the specified control points, with one-sided differences used for the first and last piece. The default [tension](#curveCardinal_tension) is 0.
681
682 <a name="curveCardinalClosed" href="#curveCardinalClosed">#</a> d3.<b>curveCardinalClosed</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/cardinalClosed.js "Source")
683
684 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/cardinalClosed.png" width="888" height="240" alt="cardinalClosed">
685
686 Produces a closed cubic [cardinal spline](https://en.wikipedia.org/wiki/Cubic_Hermite_spline#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](#curveCardinal_tension) is 0.
687
688 <a name="curveCardinalOpen" href="#curveCardinalOpen">#</a> d3.<b>curveCardinalOpen</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/cardinalOpen.js "Source")
689
690 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/cardinalOpen.png" width="888" height="240" alt="cardinalOpen">
691
692 Produces a cubic [cardinal spline](https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline) using the specified control points. Unlike [curveCardinal](#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](#curveCardinal_tension) is 0.
693
694 <a name="curveCardinal_tension" href="#curveCardinal_tension">#</a> <i>cardinal</i>.<b>tension</b>(<i>tension</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/cardinalOpen.js#L44 "Source")
695
696 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](#curveLinear); a *tension* of zero produces a uniform [Catmull–Rom](#curveCatmullRom) spline. For example:
697
698 ```js
699 var line = d3.line().curve(d3.curveCardinal.tension(0.5));
700 ```
701
702 <a name="curveCatmullRom" href="#curveCatmullRom">#</a> d3.<b>curveCatmullRom</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/catmullRom.js "Source")
703
704 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/catmullRom.png" width="888" height="240" alt="catmullRom">
705
706 Produces a cubic Catmull–Rom spline using the specified control points and the parameter [*alpha*](#catmullRom_alpha), which defaults to 0.5, as proposed by Yuksel et al. in [On the Parameterization of Catmull–Rom Curves](http://www.cemyuksel.com/research/catmullrom_param/), with one-sided differences used for the first and last piece.
707
708 <a name="curveCatmullRomClosed" href="#curveCatmullRomClosed">#</a> d3.<b>curveCatmullRomClosed</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/catmullRomClosed.js "Source")
709
710 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/catmullRomClosed.png" width="888" height="330" alt="catmullRomClosed">
711
712 Produces a closed cubic Catmull–Rom spline using the specified control points and the parameter [*alpha*](#catmullRom_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.
713
714 <a name="curveCatmullRomOpen" href="#curveCatmullRomOpen">#</a> d3.<b>curveCatmullRomOpen</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/catmullRomOpen.js "Source")
715
716 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/catmullRomOpen.png" width="888" height="240" alt="catmullRomOpen">
717
718 Produces a cubic Catmull–Rom spline using the specified control points and the parameter [*alpha*](#catmullRom_alpha), which defaults to 0.5, as proposed by Yuksel et al. Unlike [curveCatmullRom](#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.
719
720 <a name="curveCatmullRom_alpha" href="#curveCatmullRom_alpha">#</a> <i>catmullRom</i>.<b>alpha</b>(<i>alpha</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/catmullRom.js#L83 "Source")
721
722 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](#curveCardinal) with a tension of zero; if *alpha* is one, produces a chordal spline; if *alpha* is 0.5, produces a [centripetal spline](https://en.wikipedia.org/wiki/Centripetal_Catmull–Rom_spline). Centripetal splines are recommended to avoid self-intersections and overshoot. For example:
723
724 ```js
725 var line = d3.line().curve(d3.curveCatmullRom.alpha(0.5));
726 ```
727
728 <a name="curveLinear" href="#curveLinear">#</a> d3.<b>curveLinear</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/linear.js "Source")
729
730 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/linear.png" width="888" height="240" alt="linear">
731
732 Produces a polyline through the specified points.
733
734 <a name="curveLinearClosed" href="#curveLinearClosed">#</a> d3.<b>curveLinearClosed</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/linearClosed.js "Source")
735
736 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/linearClosed.png" width="888" height="240" alt="linearClosed">
737
738 Produces a closed polyline through the specified points by repeating the first point when the line segment ends.
739
740 <a name="curveMonotoneX" href="#curveMonotoneX">#</a> d3.<b>curveMonotoneX</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/monotone.js#L98 "Source")
741
742 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/monotoneX.png" width="888" height="240" alt="monotoneX">
743
744 Produces a cubic spline that [preserves monotonicity](https://en.wikipedia.org/wiki/Monotone_cubic_interpolation) in *y*, assuming monotonicity in *x*, as proposed by Steffen in [A simple method for monotonic interpolation in one dimension](http://adsabs.harvard.edu/full/1990A%26A...239..443S): “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.”
745
746 <a name="curveMonotoneY" href="#curveMonotoneY">#</a> d3.<b>curveMonotoneY</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/monotone.js#L102 "Source")
747
748 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/monotoneY.png" width="888" height="240" alt="monotoneY">
749
750 Produces a cubic spline that [preserves monotonicity](https://en.wikipedia.org/wiki/Monotone_cubic_interpolation) in *x*, assuming monotonicity in *y*, as proposed by Steffen in [A simple method for monotonic interpolation in one dimension](http://adsabs.harvard.edu/full/1990A%26A...239..443S): “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.”
751
752 <a name="curveNatural" href="#curveNatural">#</a> d3.<b>curveNatural</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/natural.js "Source")
753
754 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/natural.png" width="888" height="240" alt="natural">
755
756 Produces a [natural](https://en.wikipedia.org/wiki/Spline_interpolation) [cubic spline](http://mathworld.wolfram.com/CubicSpline.html) with the second derivative of the spline set to zero at the endpoints.
757
758 <a name="curveStep" href="#curveStep">#</a> d3.<b>curveStep</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/step.js "Source")
759
760 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/step.png" width="888" height="240" alt="step">
761
762 Produces a piecewise constant function (a [step function](https://en.wikipedia.org/wiki/Step_function)) consisting of alternating horizontal and vertical lines. The *y*-value changes at the midpoint of each pair of adjacent *x*-values.
763
764 <a name="curveStepAfter" href="#curveStepAfter">#</a> d3.<b>curveStepAfter</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/step.js#L51 "Source")
765
766 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/stepAfter.png" width="888" height="240" alt="stepAfter">
767
768 Produces a piecewise constant function (a [step function](https://en.wikipedia.org/wiki/Step_function)) consisting of alternating horizontal and vertical lines. The *y*-value changes after the *x*-value.
769
770 <a name="curveStepBefore" href="#curveStepBefore">#</a> d3.<b>curveStepBefore</b>(<i>context</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/step.js#L47 "Source")
771
772 <img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/stepBefore.png" width="888" height="240" alt="stepBefore">
773
774 Produces a piecewise constant function (a [step function](https://en.wikipedia.org/wiki/Step_function)) consisting of alternating horizontal and vertical lines. The *y*-value changes before the *x*-value.
775
776 ### Custom Curves
777
778 Curves are typically not used directly, instead being passed to [*line*.curve](#line_curve) and [*area*.curve](#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.
779
780 <a name="curve_areaStart" href="#curve_areaStart">#</a> <i>curve</i>.<b>areaStart</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/curve/step.js#L7 "Source")
781
782 Indicates the start of a new area segment. Each area segment consists of exactly two [line segments](#curve_lineStart): the topline, followed by the baseline, with the baseline points in reverse order.
783
784 <a name="curve_areaEnd" href="#curve_areaEnd">#</a> <i>curve</i>.<b>areaEnd</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/curve/step.js#L10 "Source")
785
786 Indicates the end of the current area segment.
787
788 <a name="curve_lineStart" href="#curve_lineStart">#</a> <i>curve</i>.<b>lineStart</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/curve/step.js#L13 "Source")
789
790 Indicates the start of a new line segment. Zero or more [points](#curve_point) will follow.
791
792 <a name="curve_lineEnd" href="#curve_lineEnd">#</a> <i>curve</i>.<b>lineEnd</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/curve/step.js#L17 "Source")
793
794 Indicates the end of the current line segment.
795
796 <a name="curve_point" href="#curve_point">#</a> <i>curve</i>.<b>point</b>(<i>x</i>, <i>y</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/curve/step.js#L22 "Source")
797
798 Indicates a new point in the current line segment with the given *x*- and *y*-values.
799
800 ### Links
801
802 [<img alt="Tidy Tree" src="https://raw.githubusercontent.com/d3/d3-hierarchy/master/img/tree.png">](http://bl.ocks.org/mbostock/9d0899acb5d3b8d839d9d613a9e1fe04)
803
804 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](#linkVertical), [horizontal](#linkHorizontal) or [radial](#linkRadial).
805
806 <a name="linkVertical" href="#linkVertical">#</a> d3.<b>linkVertical</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/link/index.js#L74 "Source")
807
808 Returns a new [link generator](#_link) with vertical tangents. For example, to visualize [links](https://github.com/d3/d3-hierarchy/blob/master/README.md#node_links) in a [tree diagram](https://github.com/d3/d3-hierarchy/blob/master/README.md#tree) rooted on the top edge of the display, you might say:
809
810 ```js
811 var link = d3.linkVertical()
812 .x(function(d) { return d.x; })
813 .y(function(d) { return d.y; });
814 ```
815
816 <a name="linkHorizontal" href="#linkHorizontal">#</a> d3.<b>linkHorizontal</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/link/index.js#L70 "Source")
817
818 Returns a new [link generator](#_link) with horizontal tangents. For example, to visualize [links](https://github.com/d3/d3-hierarchy/blob/master/README.md#node_links) in a [tree diagram](https://github.com/d3/d3-hierarchy/blob/master/README.md#tree) rooted on the left edge of the display, you might say:
819
820 ```js
821 var link = d3.linkHorizontal()
822 .x(function(d) { return d.y; })
823 .y(function(d) { return d.x; });
824 ```
825
826 <a href="#_link" name="_link">#</a> <i>link</i>(<i>arguments…</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/link/index.js#L21 "Source")
827
828 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:
829
830 ```js
831 link({
832 source: [100, 100],
833 target: [300, 300]
834 });
835 ```
836
837 <a name="link_source" href="#link_source">#</a> <i>link</i>.<b>source</b>([<i>source</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/link/index.js#L28 "Source")
838
839 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:
840
841 ```js
842 function source(d) {
843 return d.source;
844 }
845 ```
846
847 <a name="link_target" href="#link_target">#</a> <i>link</i>.<b>target</b>([<i>target</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/link/index.js#L32 "Source")
848
849 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:
850
851 ```js
852 function target(d) {
853 return d.target;
854 }
855 ```
856
857 <a name="link_x" href="#link_x">#</a> <i>link</i>.<b>x</b>([<i>x</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/link/index.js#L36 "Source")
858
859 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:
860
861 ```js
862 function x(d) {
863 return d[0];
864 }
865 ```
866
867 <a name="link_y" href="#link_y">#</a> <i>link</i>.<b>y</b>([<i>y</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/link/index.js#L40 "Source")
868
869 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:
870
871 ```js
872 function y(d) {
873 return d[1];
874 }
875 ```
876
877 <a name="link_context" href="#link_context">#</a> <i>link</i>.<b>context</b>([<i>context</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/link/index.js#L44 "Source")
878
879 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](#_link) is rendered to this context as a sequence of [path method](http://www.w3.org/TR/2dcontext/#canvaspathmethods) calls. Otherwise, a [path data](http://www.w3.org/TR/SVG/paths.html#PathData) string representing the generated link is returned. See also [d3-path](https://github.com/d3/d3-path).
880
881 <a name="linkRadial" href="#linkRadial">#</a> d3.<b>linkRadial</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/link/index.js#L78 "Source")
882
883 Returns a new [link generator](#_link) with radial tangents. For example, to visualize [links](https://github.com/d3/d3-hierarchy/blob/master/README.md#node_links) in a [tree diagram](https://github.com/d3/d3-hierarchy/blob/master/README.md#tree) rooted in the center of the display, you might say:
884
885 ```js
886 var link = d3.linkRadial()
887 .angle(function(d) { return d.x; })
888 .radius(function(d) { return d.y; });
889 ```
890
891 <a name="linkRadial_angle" href="#linkRadial_angle">#</a> <i>linkRadial</i>.<b>angle</b>([<i>angle</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/link/index.js#L80 "Source")
892
893 Equivalent to [*link*.x](#link_x), except the accessor returns the angle in radians, with 0 at -*y* (12 o’clock).
894
895 <a name="linkRadial_radius" href="#linkRadial_radius">#</a> <i>linkRadial</i>.<b>radius</b>([<i>radius</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/link/index.js#L81 "Source")
896
897 Equivalent to [*link*.y](#link_y), except the accessor returns the radius: the distance from the origin ⟨0,0⟩.
898
899 ### Symbols
900
901 <a href="#symbolCircle"><img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/circle.png" width="100" height="100"></a><a href="#symbolCross"><img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/cross.png" width="100" height="100"></a><a href="#symbolDiamond"><img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/diamond.png" width="100" height="100"></a><a href="#symbolSquare"><img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/square.png" width="100" height="100"></a><a href="#symbolStar"><img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/star.png" width="100" height="100"></a><a href="#symbolTriangle"><img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/triangle.png" width="100" height="100"><a href="#symbolWye"><img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/wye.png" width="100" height="100"></a>
902
903 Symbols provide a categorical shape encoding as is commonly used in scatterplots. Symbols are always centered at ⟨0,0⟩; use a transform (see: [SVG](http://www.w3.org/TR/SVG/coords.html#TransformAttribute), [Canvas](http://www.w3.org/TR/2dcontext/#transformations)) to move the symbol to a different position.
904
905 <a name="symbol" href="#symbol">#</a> d3.<b>symbol</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/symbol.js "Source")
906
907 Constructs a new symbol generator with the default settings.
908
909 <a name="_symbol" href="#_symbol">#</a> <i>symbol</i>(<i>arguments</i>…) [<>](https://github.com/d3/d3-shape/blob/master/src/symbol.js#L11 "Source")
910
911 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](#symbol_context), then the symbol is rendered to this context as a sequence of [path method](http://www.w3.org/TR/2dcontext/#canvaspathmethods) calls and this function returns void. Otherwise, a [path data](http://www.w3.org/TR/SVG/paths.html#PathData) string is returned.
912
913 <a name="symbol_type" href="#symbol_type">#</a> <i>symbol</i>.<b>type</b>([<i>type</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/symbol.js#L33 "Source")
914
915 If *type* is specified, sets the symbol type to the specified function or symbol type and returns this line generator. If *type* is not specified, returns the current symbol type accessor, which defaults to:
916
917 ```js
918 function type() {
919 return circle;
920 }
921 ```
922
923 See [symbols](#symbols) for the set of built-in symbol types. To implement a custom symbol type, pass an object that implements [*symbolType*.draw](#symbolType_draw).
924
925 <a name="symbol_size" href="#symbol_size">#</a> <i>symbol</i>.<b>size</b>([<i>size</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/symbol.js#L37 "Source")
926
927 If *size* is specified, sets the size to the specified function or number and returns this symbol generator. If *size* is not specified, returns the current size accessor, which defaults to:
928
929 ```js
930 function size() {
931 return 64;
932 }
933 ```
934
935 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](http://bl.ocks.org/mbostock/3dd515e692504c92ab65).
936
937 <a name="symbol_context" href="#symbol_context">#</a> <i>symbol</i>.<b>context</b>([<i>context</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/symbol.js#L41 "Source")
938
939 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](#_symbol) is rendered to this context as a sequence of [path method](http://www.w3.org/TR/2dcontext/#canvaspathmethods) calls. Otherwise, a [path data](http://www.w3.org/TR/SVG/paths.html#PathData) string representing the generated symbol is returned.
940
941 <a name="symbols" href="#symbols">#</a> d3.<b>symbols</b>
942
943 An array containing the set of all built-in symbol types: [circle](#symbolCircle), [cross](#symbolCross), [diamond](#symbolDiamond), [square](#symbolSquare), [star](#symbolStar), [triangle](#symbolTriangle), and [wye](#symbolWye). Useful for constructing the range of an [ordinal scale](https://github.com/d3/d3-scale#ordinal-scales) should you wish to use a shape encoding for categorical data.
944
945 <a name="symbolCircle" href="#symbolCircle">#</a> d3.<b>symbolCircle</b> [<>](https://github.com/d3/d3-shape/blob/master/src/symbol/circle.js "Source")
946
947 The circle symbol type.
948
949 <a name="symbolCross" href="#symbolCross">#</a> d3.<b>symbolCross</b> [<>](https://github.com/d3/d3-shape/blob/master/src/symbol/cross.js "Source")
950
951 The Greek cross symbol type, with arms of equal length.
952
953 <a name="symbolDiamond" href="#symbolDiamond">#</a> d3.<b>symbolDiamond</b> [<>](https://github.com/d3/d3-shape/blob/master/src/symbol/diamond.js "Source")
954
955 The rhombus symbol type.
956
957 <a name="symbolSquare" href="#symbolSquare">#</a> d3.<b>symbolSquare</b> [<>](https://github.com/d3/d3-shape/blob/master/src/symbol/square.js "Source")
958
959 The square symbol type.
960
961 <a name="symbolStar" href="#symbolStar">#</a> d3.<b>symbolStar</b> [<>](https://github.com/d3/d3-shape/blob/master/src/symbol/star.js "Source")
962
963 The pentagonal star (pentagram) symbol type.
964
965 <a name="symbolTriangle" href="#symbolTriangle">#</a> d3.<b>symbolTriangle</b> [<>](https://github.com/d3/d3-shape/blob/master/src/symbol/triangle.js "Source")
966
967 The up-pointing triangle symbol type.
968
969 <a name="symbolWye" href="#symbolWye">#</a> d3.<b>symbolWye</b> [<>](https://github.com/d3/d3-shape/blob/master/src/symbol/wye.js "Source")
970
971 The Y-shape symbol type.
972
973 <a name="pointRadial" href="#pointRadial">#</a> d3.<b>pointRadial</b>(<i>angle</i>, <i>radius</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/pointRadial.js "Source")
974
975 Returns the point [<i>x</i>, <i>y</i>] for the given *angle* in radians, with 0 at -*y* (12 o’clock) and positive angles proceeding clockwise, and the given *radius*.
976
977 ### Custom Symbol Types
978
979 Symbol types are typically not used directly, instead being passed to [*symbol*.type](#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.
980
981 <a name="symbolType_draw" href="#symbolType_draw">#</a> <i>symbolType</i>.<b>draw</b>(<i>context</i>, <i>size</i>)
982
983 Renders this symbol type to the specified *context* with the specified *size* in square pixels. The *context* implements the [CanvasPathMethods](http://www.w3.org/TR/2dcontext/#canvaspathmethods) interface. (Note that this is a subset of the CanvasRenderingContext2D interface!)
984
985 ### Stacks
986
987 [<img alt="Stacked Bar Chart" src="https://raw.githubusercontent.com/d3/d3-shape/master/img/stacked-bar.png" width="295" height="154">](http://bl.ocks.org/mbostock/3886208)[<img alt="Streamgraph" src="https://raw.githubusercontent.com/d3/d3-shape/master/img/stacked-stream.png" width="295" height="154">](http://bl.ocks.org/mbostock/4060954)
988
989 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.
990
991 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](#stack_order) carefully, and consider a [streamgraph](#stackOffsetWiggle). (See also [grouped charts](http://bl.ocks.org/mbostock/3887051).)
992
993 Like the [pie generator](#pies), the stack generator does not produce a shape directly. Instead it computes positions which you can then pass to an [area generator](#areas) or use directly, say to position bars.
994
995 <a name="stack" href="#stack">#</a> d3.<b>stack</b>() [<>](https://github.com/d3/d3-shape/blob/master/src/stack.js "Source")
996
997 Constructs a new stack generator with the default settings.
998
999 <a name="_stack" href="#_stack">#</a> <i>stack</i>(<i>data</i>[, <i>arguments…</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/stack.js#L16 "Source")
1000
1001 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.
1002
1003 The series are determined by the [keys accessor](#stack_keys); each series *i* in the returned array corresponds to the *i*th key. Each series is an array of points, where each point *j* corresponds to the *j*th 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](#stack_value) for this point. The key for each series is available as *series*.key, and the [index](#stack_order) as *series*.index. The input data element for each point is available as *point*.data.
1004
1005 For example, consider the following table representing monthly sales of fruits:
1006
1007 Month | Apples | Bananas | Cherries | Dates
1008 --------|--------|---------|----------|-------
1009 1/2015 | 3840 | 1920 | 960 | 400
1010 2/2015 | 1600 | 1440 | 960 | 400
1011 3/2015 | 640 | 960 | 640 | 400
1012 4/2015 | 320 | 480 | 640 | 400
1013
1014 This might be represented in JavaScript as an array of objects:
1015
1016 ```js
1017 var data = [
1018 {month: new Date(2015, 0, 1), apples: 3840, bananas: 1920, cherries: 960, dates: 400},
1019 {month: new Date(2015, 1, 1), apples: 1600, bananas: 1440, cherries: 960, dates: 400},
1020 {month: new Date(2015, 2, 1), apples: 640, bananas: 960, cherries: 640, dates: 400},
1021 {month: new Date(2015, 3, 1), apples: 320, bananas: 480, cherries: 640, dates: 400}
1022 ];
1023 ```
1024
1025 To produce a stack for this data:
1026
1027 ```js
1028 var stack = d3.stack()
1029 .keys(["apples", "bananas", "cherries", "dates"])
1030 .order(d3.stackOrderNone)
1031 .offset(d3.stackOffsetNone);
1032
1033 var series = stack(data);
1034 ```
1035
1036 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:
1037
1038 ```js
1039 [
1040 [[ 0, 3840], [ 0, 1600], [ 0, 640], [ 0, 320]], // apples
1041 [[3840, 5760], [1600, 3040], [ 640, 1600], [ 320, 800]], // bananas
1042 [[5760, 6720], [3040, 4000], [1600, 2240], [ 800, 1440]], // cherries
1043 [[6720, 7120], [4000, 4400], [2240, 2640], [1440, 1840]], // dates
1044 ]
1045 ```
1046
1047 Each series in then typically passed to an [area generator](#areas) to render an area chart, or used to construct rectangles for a bar chart.
1048
1049 <a name="stack_keys" href="#stack_keys">#</a> <i>stack</i>.<b>keys</b>([<i>keys</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/stack.js#L40 "Source")
1050
1051 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](#_stack) for each key. Keys are typically strings, but they may be arbitrary values. The series’ key is passed to the [value accessor](#stack_value), along with each data point, to compute the point’s value.
1052
1053 <a name="stack_value" href="#stack_value">#</a> <i>stack</i>.<b>value</b>([<i>value</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/stack.js#L44 "Source")
1054
1055 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:
1056
1057 ```js
1058 function value(d, key) {
1059 return d[key];
1060 }
1061 ```
1062
1063 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*](#_stack) for an example.
1064
1065 <a name="stack_order" href="#stack_order">#</a> <i>stack</i>.<b>order</b>([<i>order</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/stack.js#L48 "Source")
1066
1067 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 acccesor, which defaults to [stackOrderNone](#stackOrderNone); this uses the order given by the [key accessor](#stack_key). See [stack orders](#stack-orders) for the built-in orders.
1068
1069 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:
1070
1071 ```js
1072 function orderNone(series) {
1073 var n = series.length, o = new Array(n);
1074 while (--n >= 0) o[n] = n;
1075 return o;
1076 }
1077 ```
1078
1079 The stack order is computed prior to the [offset](#stack_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.
1080
1081 <a name="stack_offset" href="#stack_offset">#</a> <i>stack</i>.<b>offset</b>([<i>offset</i>]) [<>](https://github.com/d3/d3-shape/blob/master/src/stack.js#L52 "Source")
1082
1083 If *offset* is specified, sets the offset accessor to the specified function or array and returns this stack generator. If *offset* is not specified, returns the current offset acccesor, which defaults to [stackOffsetNone](#stackOffsetNone); this uses a zero baseline. See [stack offsets](#stack-offsets) for the built-in offsets.
1084
1085 If *offset* is a function, it is passed the generated series array and the order index array. The offset function is then responsible for updating the lower and upper values in the series array to layout the stack. For example, the default offset is defined as:
1086
1087 ```js
1088 function offsetNone(series, order) {
1089 if (!((n = series.length) > 1)) return;
1090 for (var i = 1, s0, s1 = series[order[0]], n, m = s1.length; i < n; ++i) {
1091 s0 = s1, s1 = series[order[i]];
1092 for (var j = 0; j < m; ++j) {
1093 s1[j][1] += s1[j][0] = s0[j][1];
1094 }
1095 }
1096 }
1097 ```
1098
1099 ### Stack Orders
1100
1101 Stack orders are typically not used directly, but are instead passed to [*stack*.order](#stack_order).
1102
1103 <a name="stackOrderAscending" href="#stackOrderAscending">#</a> d3.<b>stackOrderAscending</b>(<i>series</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/order/ascending.js "Source")
1104
1105 Returns a series order such that the smallest series (according to the sum of values) is at the bottom.
1106
1107 <a name="stackOrderDescending" href="#stackOrderDescending">#</a> d3.<b>stackOrderDescending</b>(<i>series</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/order/descending.js "Source")
1108
1109 Returns a series order such that the largest series (according to the sum of values) is at the bottom.
1110
1111 <a name="stackOrderInsideOut" href="#stackOrderInsideOut">#</a> d3.<b>stackOrderInsideOut</b>(<i>series</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/order/insideOut.js "Source")
1112
1113 Returns a series order such that the larger series (according to the sum of values) are on the inside and the smaller series are on the outside. This order is recommended for streamgraphs in conjunction with the [wiggle offset](#stackOffsetWiggle). See [Stacked Graphs—Geometry & Aesthetics](http://leebyron.com/streamgraph/) by Byron & Wattenberg for more information.
1114
1115 <a name="stackOrderNone" href="#stackOrderNone">#</a> d3.<b>stackOrderNone</b>(<i>series</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/order/none.js "Source")
1116
1117 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](#stack_keys).
1118
1119 <a name="stackOrderReverse" href="#stackOrderReverse">#</a> d3.<b>stackOrderReverse</b>(<i>series</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/order/reverse.js "Source")
1120
1121 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_keys).
1122
1123 ### Stack Offsets
1124
1125 Stack offsets are typically not used directly, but are instead passed to [*stack*.offset](#stack_offset).
1126
1127 <a name="stackOffsetExpand" href="#stackOffsetExpand">#</a> d3.<b>stackOffsetExpand</b>(<i>series</i>, <i>order</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/offset/expand.js "Source")
1128
1129 Applies a zero baseline and normalizes the values for each point such that the topline is always one.
1130
1131 <a name="stackOffsetDiverging" href="#stackOffsetDiverging">#</a> d3.<b>stackOffsetDiverging</b>(<i>series</i>, <i>order</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/offset/diverging.js "Source")
1132
1133 Positive values are stacked above zero, while negative values are [stacked below zero](https://bl.ocks.org/mbostock/b5935342c6d21928111928401e2c8608).
1134
1135 <a name="stackOffsetNone" href="#stackOffsetNone">#</a> d3.<b>stackOffsetNone</b>(<i>series</i>, <i>order</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/offset/none.js "Source")
1136
1137 Applies a zero baseline.
1138
1139 <a name="stackOffsetSilhouette" href="#stackOffsetSilhouette">#</a> d3.<b>stackOffsetSilhouette</b>(<i>series</i>, <i>order</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/offset/silhouette.js "Source")
1140
1141 Shifts the baseline down such that the center of the streamgraph is always at zero.
1142
1143 <a name="stackOffsetWiggle" href="#stackOffsetWiggle">#</a> d3.<b>stackOffsetWiggle</b>(<i>series</i>, <i>order</i>) [<>](https://github.com/d3/d3-shape/blob/master/src/offset/wiggle.js "Source")
1144
1145 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](#stackOrderInsideOut). See [Stacked Graphs—Geometry & Aesthetics](http://leebyron.com/streamgraph/) by Bryon & Wattenberg for more information.
0 {
1 "folders": [
2 {
3 "path": ".",
4 "file_exclude_patterns": [
5 "*.sublime-workspace"
6 ],
7 "folder_exclude_patterns": [
8 "build"
9 ]
10 }
11 ]
12 }
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 export {default as arc} from "./src/arc";
1 export {default as area} from "./src/area";
2 export {default as line} from "./src/line";
3 export {default as pie} from "./src/pie";
4 export {default as areaRadial, default as radialArea} from "./src/areaRadial"; // Note: radialArea is deprecated!
5 export {default as lineRadial, default as radialLine} from "./src/lineRadial"; // Note: radialLine is deprecated!
6 export {default as pointRadial} from "./src/pointRadial";
7 export {linkHorizontal, linkVertical, linkRadial} from "./src/link/index";
8
9 export {default as symbol, symbols} from "./src/symbol";
10 export {default as symbolCircle} from "./src/symbol/circle";
11 export {default as symbolCross} from "./src/symbol/cross";
12 export {default as symbolDiamond} from "./src/symbol/diamond";
13 export {default as symbolSquare} from "./src/symbol/square";
14 export {default as symbolStar} from "./src/symbol/star";
15 export {default as symbolTriangle} from "./src/symbol/triangle";
16 export {default as symbolWye} from "./src/symbol/wye";
17
18 export {default as curveBasisClosed} from "./src/curve/basisClosed";
19 export {default as curveBasisOpen} from "./src/curve/basisOpen";
20 export {default as curveBasis} from "./src/curve/basis";
21 export {default as curveBundle} from "./src/curve/bundle";
22 export {default as curveCardinalClosed} from "./src/curve/cardinalClosed";
23 export {default as curveCardinalOpen} from "./src/curve/cardinalOpen";
24 export {default as curveCardinal} from "./src/curve/cardinal";
25 export {default as curveCatmullRomClosed} from "./src/curve/catmullRomClosed";
26 export {default as curveCatmullRomOpen} from "./src/curve/catmullRomOpen";
27 export {default as curveCatmullRom} from "./src/curve/catmullRom";
28 export {default as curveLinearClosed} from "./src/curve/linearClosed";
29 export {default as curveLinear} from "./src/curve/linear";
30 export {monotoneX as curveMonotoneX, monotoneY as curveMonotoneY} from "./src/curve/monotone";
31 export {default as curveNatural} from "./src/curve/natural";
32 export {default as curveStep, stepAfter as curveStepAfter, stepBefore as curveStepBefore} from "./src/curve/step";
33
34 export {default as stack} from "./src/stack";
35 export {default as stackOffsetExpand} from "./src/offset/expand";
36 export {default as stackOffsetDiverging} from "./src/offset/diverging";
37 export {default as stackOffsetNone} from "./src/offset/none";
38 export {default as stackOffsetSilhouette} from "./src/offset/silhouette";
39 export {default as stackOffsetWiggle} from "./src/offset/wiggle";
40 export {default as stackOrderAscending} from "./src/order/ascending";
41 export {default as stackOrderDescending} from "./src/order/descending";
42 export {default as stackOrderInsideOut} from "./src/order/insideOut";
43 export {default as stackOrderNone} from "./src/order/none";
44 export {default as stackOrderReverse} from "./src/order/reverse";
0 {
1 "name": "d3-shape",
2 "version": "1.2.0",
3 "description": "Graphical primitives for visualization, such as lines and areas.",
4 "keywords": [
5 "d3",
6 "d3-module",
7 "graphics",
8 "visualization",
9 "canvas",
10 "svg"
11 ],
12 "homepage": "https://d3js.org/d3-shape/",
13 "license": "BSD-3-Clause",
14 "author": {
15 "name": "Mike Bostock",
16 "url": "http://bost.ocks.org/mike"
17 },
18 "main": "build/d3-shape.js",
19 "module": "index",
20 "jsnext:main": "index",
21 "repository": {
22 "type": "git",
23 "url": "https://github.com/d3/d3-shape.git"
24 },
25 "scripts": {
26 "pretest": "rm -rf build && mkdir build && rollup --banner \"$(preamble)\" -f umd -g d3-path:d3 -n d3 -o build/d3-shape.js -- index.js",
27 "test": "tape 'test/**/*-test.js' && eslint index.js src",
28 "prepublish": "npm run test && uglifyjs --preamble \"$(preamble)\" build/d3-shape.js -c -m -o build/d3-shape.min.js",
29 "postpublish": "git push && git push --tags && cd ../d3.github.com && git pull && cp ../d3-shape/build/d3-shape.js d3-shape.v1.js && cp ../d3-shape/build/d3-shape.min.js d3-shape.v1.min.js && git add d3-shape.v1.js d3-shape.v1.min.js && git commit -m \"d3-shape ${npm_package_version}\" && git push && cd - && zip -j build/d3-shape.zip -- LICENSE README.md build/d3-shape.js build/d3-shape.min.js"
30 },
31 "dependencies": {
32 "d3-path": "1"
33 },
34 "devDependencies": {
35 "d3-polygon": "1",
36 "eslint": "3",
37 "package-preamble": "0.1",
38 "rollup": "0.41",
39 "tape": "4",
40 "uglify-js": "^2.8.11"
41 }
42 }
0 import {path} from "d3-path";
1 import constant from "./constant";
2 import {abs, acos, asin, atan2, cos, epsilon, halfPi, max, min, pi, sin, sqrt, tau} from "./math";
3
4 function arcInnerRadius(d) {
5 return d.innerRadius;
6 }
7
8 function arcOuterRadius(d) {
9 return d.outerRadius;
10 }
11
12 function arcStartAngle(d) {
13 return d.startAngle;
14 }
15
16 function arcEndAngle(d) {
17 return d.endAngle;
18 }
19
20 function arcPadAngle(d) {
21 return d && d.padAngle; // Note: optional!
22 }
23
24 function intersect(x0, y0, x1, y1, x2, y2, x3, y3) {
25 var x10 = x1 - x0, y10 = y1 - y0,
26 x32 = x3 - x2, y32 = y3 - y2,
27 t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / (y32 * x10 - x32 * y10);
28 return [x0 + t * x10, y0 + t * y10];
29 }
30
31 // Compute perpendicular offset line of length rc.
32 // http://mathworld.wolfram.com/Circle-LineIntersection.html
33 function cornerTangents(x0, y0, x1, y1, r1, rc, cw) {
34 var x01 = x0 - x1,
35 y01 = y0 - y1,
36 lo = (cw ? rc : -rc) / sqrt(x01 * x01 + y01 * y01),
37 ox = lo * y01,
38 oy = -lo * x01,
39 x11 = x0 + ox,
40 y11 = y0 + oy,
41 x10 = x1 + ox,
42 y10 = y1 + oy,
43 x00 = (x11 + x10) / 2,
44 y00 = (y11 + y10) / 2,
45 dx = x10 - x11,
46 dy = y10 - y11,
47 d2 = dx * dx + dy * dy,
48 r = r1 - rc,
49 D = x11 * y10 - x10 * y11,
50 d = (dy < 0 ? -1 : 1) * sqrt(max(0, r * r * d2 - D * D)),
51 cx0 = (D * dy - dx * d) / d2,
52 cy0 = (-D * dx - dy * d) / d2,
53 cx1 = (D * dy + dx * d) / d2,
54 cy1 = (-D * dx + dy * d) / d2,
55 dx0 = cx0 - x00,
56 dy0 = cy0 - y00,
57 dx1 = cx1 - x00,
58 dy1 = cy1 - y00;
59
60 // Pick the closer of the two intersection points.
61 // TODO Is there a faster way to determine which intersection to use?
62 if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;
63
64 return {
65 cx: cx0,
66 cy: cy0,
67 x01: -ox,
68 y01: -oy,
69 x11: cx0 * (r1 / r - 1),
70 y11: cy0 * (r1 / r - 1)
71 };
72 }
73
74 export default function() {
75 var innerRadius = arcInnerRadius,
76 outerRadius = arcOuterRadius,
77 cornerRadius = constant(0),
78 padRadius = null,
79 startAngle = arcStartAngle,
80 endAngle = arcEndAngle,
81 padAngle = arcPadAngle,
82 context = null;
83
84 function arc() {
85 var buffer,
86 r,
87 r0 = +innerRadius.apply(this, arguments),
88 r1 = +outerRadius.apply(this, arguments),
89 a0 = startAngle.apply(this, arguments) - halfPi,
90 a1 = endAngle.apply(this, arguments) - halfPi,
91 da = abs(a1 - a0),
92 cw = a1 > a0;
93
94 if (!context) context = buffer = path();
95
96 // Ensure that the outer radius is always larger than the inner radius.
97 if (r1 < r0) r = r1, r1 = r0, r0 = r;
98
99 // Is it a point?
100 if (!(r1 > epsilon)) context.moveTo(0, 0);
101
102 // Or is it a circle or annulus?
103 else if (da > tau - epsilon) {
104 context.moveTo(r1 * cos(a0), r1 * sin(a0));
105 context.arc(0, 0, r1, a0, a1, !cw);
106 if (r0 > epsilon) {
107 context.moveTo(r0 * cos(a1), r0 * sin(a1));
108 context.arc(0, 0, r0, a1, a0, cw);
109 }
110 }
111
112 // Or is it a circular or annular sector?
113 else {
114 var a01 = a0,
115 a11 = a1,
116 a00 = a0,
117 a10 = a1,
118 da0 = da,
119 da1 = da,
120 ap = padAngle.apply(this, arguments) / 2,
121 rp = (ap > epsilon) && (padRadius ? +padRadius.apply(this, arguments) : sqrt(r0 * r0 + r1 * r1)),
122 rc = min(abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),
123 rc0 = rc,
124 rc1 = rc,
125 t0,
126 t1;
127
128 // Apply padding? Note that since r1 ≥ r0, da1 ≥ da0.
129 if (rp > epsilon) {
130 var p0 = asin(rp / r0 * sin(ap)),
131 p1 = asin(rp / r1 * sin(ap));
132 if ((da0 -= p0 * 2) > epsilon) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0;
133 else da0 = 0, a00 = a10 = (a0 + a1) / 2;
134 if ((da1 -= p1 * 2) > epsilon) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1;
135 else da1 = 0, a01 = a11 = (a0 + a1) / 2;
136 }
137
138 var x01 = r1 * cos(a01),
139 y01 = r1 * sin(a01),
140 x10 = r0 * cos(a10),
141 y10 = r0 * sin(a10);
142
143 // Apply rounded corners?
144 if (rc > epsilon) {
145 var x11 = r1 * cos(a11),
146 y11 = r1 * sin(a11),
147 x00 = r0 * cos(a00),
148 y00 = r0 * sin(a00);
149
150 // Restrict the corner radius according to the sector angle.
151 if (da < pi) {
152 var oc = da0 > epsilon ? intersect(x01, y01, x00, y00, x11, y11, x10, y10) : [x10, y10],
153 ax = x01 - oc[0],
154 ay = y01 - oc[1],
155 bx = x11 - oc[0],
156 by = y11 - oc[1],
157 kc = 1 / sin(acos((ax * bx + ay * by) / (sqrt(ax * ax + ay * ay) * sqrt(bx * bx + by * by))) / 2),
158 lc = sqrt(oc[0] * oc[0] + oc[1] * oc[1]);
159 rc0 = min(rc, (r0 - lc) / (kc - 1));
160 rc1 = min(rc, (r1 - lc) / (kc + 1));
161 }
162 }
163
164 // Is the sector collapsed to a line?
165 if (!(da1 > epsilon)) context.moveTo(x01, y01);
166
167 // Does the sector’s outer ring have rounded corners?
168 else if (rc1 > epsilon) {
169 t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw);
170 t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw);
171
172 context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01);
173
174 // Have the corners merged?
175 if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);
176
177 // Otherwise, draw the two corners and the ring.
178 else {
179 context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);
180 context.arc(0, 0, r1, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), !cw);
181 context.arc(t1.cx, t1.cy, rc1, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);
182 }
183 }
184
185 // Or is the outer ring just a circular arc?
186 else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw);
187
188 // Is there no inner ring, and it’s a circular sector?
189 // Or perhaps it’s an annular sector collapsed due to padding?
190 if (!(r0 > epsilon) || !(da0 > epsilon)) context.lineTo(x10, y10);
191
192 // Does the sector’s inner ring (or point) have rounded corners?
193 else if (rc0 > epsilon) {
194 t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);
195 t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);
196
197 context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);
198
199 // Have the corners merged?
200 if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);
201
202 // Otherwise, draw the two corners and the ring.
203 else {
204 context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);
205 context.arc(0, 0, r0, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), cw);
206 context.arc(t1.cx, t1.cy, rc0, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);
207 }
208 }
209
210 // Or is the inner ring just a circular arc?
211 else context.arc(0, 0, r0, a10, a00, cw);
212 }
213
214 context.closePath();
215
216 if (buffer) return context = null, buffer + "" || null;
217 }
218
219 arc.centroid = function() {
220 var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,
221 a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi / 2;
222 return [cos(a) * r, sin(a) * r];
223 };
224
225 arc.innerRadius = function(_) {
226 return arguments.length ? (innerRadius = typeof _ === "function" ? _ : constant(+_), arc) : innerRadius;
227 };
228
229 arc.outerRadius = function(_) {
230 return arguments.length ? (outerRadius = typeof _ === "function" ? _ : constant(+_), arc) : outerRadius;
231 };
232
233 arc.cornerRadius = function(_) {
234 return arguments.length ? (cornerRadius = typeof _ === "function" ? _ : constant(+_), arc) : cornerRadius;
235 };
236
237 arc.padRadius = function(_) {
238 return arguments.length ? (padRadius = _ == null ? null : typeof _ === "function" ? _ : constant(+_), arc) : padRadius;
239 };
240
241 arc.startAngle = function(_) {
242 return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant(+_), arc) : startAngle;
243 };
244
245 arc.endAngle = function(_) {
246 return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant(+_), arc) : endAngle;
247 };
248
249 arc.padAngle = function(_) {
250 return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant(+_), arc) : padAngle;
251 };
252
253 arc.context = function(_) {
254 return arguments.length ? ((context = _ == null ? null : _), arc) : context;
255 };
256
257 return arc;
258 }
0 import {path} from "d3-path";
1 import constant from "./constant";
2 import curveLinear from "./curve/linear";
3 import line from "./line";
4 import {x as pointX, y as pointY} from "./point";
5
6 export default function() {
7 var x0 = pointX,
8 x1 = null,
9 y0 = constant(0),
10 y1 = pointY,
11 defined = constant(true),
12 context = null,
13 curve = curveLinear,
14 output = null;
15
16 function area(data) {
17 var i,
18 j,
19 k,
20 n = data.length,
21 d,
22 defined0 = false,
23 buffer,
24 x0z = new Array(n),
25 y0z = new Array(n);
26
27 if (context == null) output = curve(buffer = path());
28
29 for (i = 0; i <= n; ++i) {
30 if (!(i < n && defined(d = data[i], i, data)) === defined0) {
31 if (defined0 = !defined0) {
32 j = i;
33 output.areaStart();
34 output.lineStart();
35 } else {
36 output.lineEnd();
37 output.lineStart();
38 for (k = i - 1; k >= j; --k) {
39 output.point(x0z[k], y0z[k]);
40 }
41 output.lineEnd();
42 output.areaEnd();
43 }
44 }
45 if (defined0) {
46 x0z[i] = +x0(d, i, data), y0z[i] = +y0(d, i, data);
47 output.point(x1 ? +x1(d, i, data) : x0z[i], y1 ? +y1(d, i, data) : y0z[i]);
48 }
49 }
50
51 if (buffer) return output = null, buffer + "" || null;
52 }
53
54 function arealine() {
55 return line().defined(defined).curve(curve).context(context);
56 }
57
58 area.x = function(_) {
59 return arguments.length ? (x0 = typeof _ === "function" ? _ : constant(+_), x1 = null, area) : x0;
60 };
61
62 area.x0 = function(_) {
63 return arguments.length ? (x0 = typeof _ === "function" ? _ : constant(+_), area) : x0;
64 };
65
66 area.x1 = function(_) {
67 return arguments.length ? (x1 = _ == null ? null : typeof _ === "function" ? _ : constant(+_), area) : x1;
68 };
69
70 area.y = function(_) {
71 return arguments.length ? (y0 = typeof _ === "function" ? _ : constant(+_), y1 = null, area) : y0;
72 };
73
74 area.y0 = function(_) {
75 return arguments.length ? (y0 = typeof _ === "function" ? _ : constant(+_), area) : y0;
76 };
77
78 area.y1 = function(_) {
79 return arguments.length ? (y1 = _ == null ? null : typeof _ === "function" ? _ : constant(+_), area) : y1;
80 };
81
82 area.lineX0 =
83 area.lineY0 = function() {
84 return arealine().x(x0).y(y0);
85 };
86
87 area.lineY1 = function() {
88 return arealine().x(x0).y(y1);
89 };
90
91 area.lineX1 = function() {
92 return arealine().x(x1).y(y0);
93 };
94
95 area.defined = function(_) {
96 return arguments.length ? (defined = typeof _ === "function" ? _ : constant(!!_), area) : defined;
97 };
98
99 area.curve = function(_) {
100 return arguments.length ? (curve = _, context != null && (output = curve(context)), area) : curve;
101 };
102
103 area.context = function(_) {
104 return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), area) : context;
105 };
106
107 return area;
108 }
0 import curveRadial, {curveRadialLinear} from "./curve/radial";
1 import area from "./area";
2 import {lineRadial} from "./lineRadial"
3
4 export default function() {
5 var a = area().curve(curveRadialLinear),
6 c = a.curve,
7 x0 = a.lineX0,
8 x1 = a.lineX1,
9 y0 = a.lineY0,
10 y1 = a.lineY1;
11
12 a.angle = a.x, delete a.x;
13 a.startAngle = a.x0, delete a.x0;
14 a.endAngle = a.x1, delete a.x1;
15 a.radius = a.y, delete a.y;
16 a.innerRadius = a.y0, delete a.y0;
17 a.outerRadius = a.y1, delete a.y1;
18 a.lineStartAngle = function() { return lineRadial(x0()); }, delete a.lineX0;
19 a.lineEndAngle = function() { return lineRadial(x1()); }, delete a.lineX1;
20 a.lineInnerRadius = function() { return lineRadial(y0()); }, delete a.lineY0;
21 a.lineOuterRadius = function() { return lineRadial(y1()); }, delete a.lineY1;
22
23 a.curve = function(_) {
24 return arguments.length ? c(curveRadial(_)) : c()._curve;
25 };
26
27 return a;
28 }
0 export var slice = Array.prototype.slice;
0 export default function(x) {
1 return function constant() {
2 return x;
3 };
4 }
0 export function point(that, x, y) {
1 that._context.bezierCurveTo(
2 (2 * that._x0 + that._x1) / 3,
3 (2 * that._y0 + that._y1) / 3,
4 (that._x0 + 2 * that._x1) / 3,
5 (that._y0 + 2 * that._y1) / 3,
6 (that._x0 + 4 * that._x1 + x) / 6,
7 (that._y0 + 4 * that._y1 + y) / 6
8 );
9 }
10
11 export function Basis(context) {
12 this._context = context;
13 }
14
15 Basis.prototype = {
16 areaStart: function() {
17 this._line = 0;
18 },
19 areaEnd: function() {
20 this._line = NaN;
21 },
22 lineStart: function() {
23 this._x0 = this._x1 =
24 this._y0 = this._y1 = NaN;
25 this._point = 0;
26 },
27 lineEnd: function() {
28 switch (this._point) {
29 case 3: point(this, this._x1, this._y1); // proceed
30 case 2: this._context.lineTo(this._x1, this._y1); break;
31 }
32 if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
33 this._line = 1 - this._line;
34 },
35 point: function(x, y) {
36 x = +x, y = +y;
37 switch (this._point) {
38 case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
39 case 1: this._point = 2; break;
40 case 2: this._point = 3; this._context.lineTo((5 * this._x0 + this._x1) / 6, (5 * this._y0 + this._y1) / 6); // proceed
41 default: point(this, x, y); break;
42 }
43 this._x0 = this._x1, this._x1 = x;
44 this._y0 = this._y1, this._y1 = y;
45 }
46 };
47
48 export default function(context) {
49 return new Basis(context);
50 }
0 import noop from "../noop";
1 import {point} from "./basis";
2
3 function BasisClosed(context) {
4 this._context = context;
5 }
6
7 BasisClosed.prototype = {
8 areaStart: noop,
9 areaEnd: noop,
10 lineStart: function() {
11 this._x0 = this._x1 = this._x2 = this._x3 = this._x4 =
12 this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = NaN;
13 this._point = 0;
14 },
15 lineEnd: function() {
16 switch (this._point) {
17 case 1: {
18 this._context.moveTo(this._x2, this._y2);
19 this._context.closePath();
20 break;
21 }
22 case 2: {
23 this._context.moveTo((this._x2 + 2 * this._x3) / 3, (this._y2 + 2 * this._y3) / 3);
24 this._context.lineTo((this._x3 + 2 * this._x2) / 3, (this._y3 + 2 * this._y2) / 3);
25 this._context.closePath();
26 break;
27 }
28 case 3: {
29 this.point(this._x2, this._y2);
30 this.point(this._x3, this._y3);
31 this.point(this._x4, this._y4);
32 break;
33 }
34 }
35 },
36 point: function(x, y) {
37 x = +x, y = +y;
38 switch (this._point) {
39 case 0: this._point = 1; this._x2 = x, this._y2 = y; break;
40 case 1: this._point = 2; this._x3 = x, this._y3 = y; break;
41 case 2: this._point = 3; this._x4 = x, this._y4 = y; this._context.moveTo((this._x0 + 4 * this._x1 + x) / 6, (this._y0 + 4 * this._y1 + y) / 6); break;
42 default: point(this, x, y); break;
43 }
44 this._x0 = this._x1, this._x1 = x;
45 this._y0 = this._y1, this._y1 = y;
46 }
47 };
48
49 export default function(context) {
50 return new BasisClosed(context);
51 }
0 import {point} from "./basis";
1
2 function BasisOpen(context) {
3 this._context = context;
4 }
5
6 BasisOpen.prototype = {
7 areaStart: function() {
8 this._line = 0;
9 },
10 areaEnd: function() {
11 this._line = NaN;
12 },
13 lineStart: function() {
14 this._x0 = this._x1 =
15 this._y0 = this._y1 = NaN;
16 this._point = 0;
17 },
18 lineEnd: function() {
19 if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
20 this._line = 1 - this._line;
21 },
22 point: function(x, y) {
23 x = +x, y = +y;
24 switch (this._point) {
25 case 0: this._point = 1; break;
26 case 1: this._point = 2; break;
27 case 2: this._point = 3; var x0 = (this._x0 + 4 * this._x1 + x) / 6, y0 = (this._y0 + 4 * this._y1 + y) / 6; this._line ? this._context.lineTo(x0, y0) : this._context.moveTo(x0, y0); break;
28 case 3: this._point = 4; // proceed
29 default: point(this, x, y); break;
30 }
31 this._x0 = this._x1, this._x1 = x;
32 this._y0 = this._y1, this._y1 = y;
33 }
34 };
35
36 export default function(context) {
37 return new BasisOpen(context);
38 }
0 import {Basis} from "./basis";
1
2 function Bundle(context, beta) {
3 this._basis = new Basis(context);
4 this._beta = beta;
5 }
6
7 Bundle.prototype = {
8 lineStart: function() {
9 this._x = [];
10 this._y = [];
11 this._basis.lineStart();
12 },
13 lineEnd: function() {
14 var x = this._x,
15 y = this._y,
16 j = x.length - 1;
17
18 if (j > 0) {
19 var x0 = x[0],
20 y0 = y[0],
21 dx = x[j] - x0,
22 dy = y[j] - y0,
23 i = -1,
24 t;
25
26 while (++i <= j) {
27 t = i / j;
28 this._basis.point(
29 this._beta * x[i] + (1 - this._beta) * (x0 + t * dx),
30 this._beta * y[i] + (1 - this._beta) * (y0 + t * dy)
31 );
32 }
33 }
34
35 this._x = this._y = null;
36 this._basis.lineEnd();
37 },
38 point: function(x, y) {
39 this._x.push(+x);
40 this._y.push(+y);
41 }
42 };
43
44 export default (function custom(beta) {
45
46 function bundle(context) {
47 return beta === 1 ? new Basis(context) : new Bundle(context, beta);
48 }
49
50 bundle.beta = function(beta) {
51 return custom(+beta);
52 };
53
54 return bundle;
55 })(0.85);
0 export function point(that, x, y) {
1 that._context.bezierCurveTo(
2 that._x1 + that._k * (that._x2 - that._x0),
3 that._y1 + that._k * (that._y2 - that._y0),
4 that._x2 + that._k * (that._x1 - x),
5 that._y2 + that._k * (that._y1 - y),
6 that._x2,
7 that._y2
8 );
9 }
10
11 export function Cardinal(context, tension) {
12 this._context = context;
13 this._k = (1 - tension) / 6;
14 }
15
16 Cardinal.prototype = {
17 areaStart: function() {
18 this._line = 0;
19 },
20 areaEnd: function() {
21 this._line = NaN;
22 },
23 lineStart: function() {
24 this._x0 = this._x1 = this._x2 =
25 this._y0 = this._y1 = this._y2 = NaN;
26 this._point = 0;
27 },
28 lineEnd: function() {
29 switch (this._point) {
30 case 2: this._context.lineTo(this._x2, this._y2); break;
31 case 3: point(this, this._x1, this._y1); break;
32 }
33 if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
34 this._line = 1 - this._line;
35 },
36 point: function(x, y) {
37 x = +x, y = +y;
38 switch (this._point) {
39 case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
40 case 1: this._point = 2; this._x1 = x, this._y1 = y; break;
41 case 2: this._point = 3; // proceed
42 default: point(this, x, y); break;
43 }
44 this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
45 this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
46 }
47 };
48
49 export default (function custom(tension) {
50
51 function cardinal(context) {
52 return new Cardinal(context, tension);
53 }
54
55 cardinal.tension = function(tension) {
56 return custom(+tension);
57 };
58
59 return cardinal;
60 })(0);
0 import noop from "../noop";
1 import {point} from "./cardinal";
2
3 export function CardinalClosed(context, tension) {
4 this._context = context;
5 this._k = (1 - tension) / 6;
6 }
7
8 CardinalClosed.prototype = {
9 areaStart: noop,
10 areaEnd: noop,
11 lineStart: function() {
12 this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =
13 this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;
14 this._point = 0;
15 },
16 lineEnd: function() {
17 switch (this._point) {
18 case 1: {
19 this._context.moveTo(this._x3, this._y3);
20 this._context.closePath();
21 break;
22 }
23 case 2: {
24 this._context.lineTo(this._x3, this._y3);
25 this._context.closePath();
26 break;
27 }
28 case 3: {
29 this.point(this._x3, this._y3);
30 this.point(this._x4, this._y4);
31 this.point(this._x5, this._y5);
32 break;
33 }
34 }
35 },
36 point: function(x, y) {
37 x = +x, y = +y;
38 switch (this._point) {
39 case 0: this._point = 1; this._x3 = x, this._y3 = y; break;
40 case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;
41 case 2: this._point = 3; this._x5 = x, this._y5 = y; break;
42 default: point(this, x, y); break;
43 }
44 this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
45 this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
46 }
47 };
48
49 export default (function custom(tension) {
50
51 function cardinal(context) {
52 return new CardinalClosed(context, tension);
53 }
54
55 cardinal.tension = function(tension) {
56 return custom(+tension);
57 };
58
59 return cardinal;
60 })(0);
0 import {point} from "./cardinal";
1
2 export function CardinalOpen(context, tension) {
3 this._context = context;
4 this._k = (1 - tension) / 6;
5 }
6
7 CardinalOpen.prototype = {
8 areaStart: function() {
9 this._line = 0;
10 },
11 areaEnd: function() {
12 this._line = NaN;
13 },
14 lineStart: function() {
15 this._x0 = this._x1 = this._x2 =
16 this._y0 = this._y1 = this._y2 = NaN;
17 this._point = 0;
18 },
19 lineEnd: function() {
20 if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
21 this._line = 1 - this._line;
22 },
23 point: function(x, y) {
24 x = +x, y = +y;
25 switch (this._point) {
26 case 0: this._point = 1; break;
27 case 1: this._point = 2; break;
28 case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;
29 case 3: this._point = 4; // proceed
30 default: point(this, x, y); break;
31 }
32 this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
33 this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
34 }
35 };
36
37 export default (function custom(tension) {
38
39 function cardinal(context) {
40 return new CardinalOpen(context, tension);
41 }
42
43 cardinal.tension = function(tension) {
44 return custom(+tension);
45 };
46
47 return cardinal;
48 })(0);
0 import {epsilon} from "../math";
1 import {Cardinal} from "./cardinal";
2
3 export function point(that, x, y) {
4 var x1 = that._x1,
5 y1 = that._y1,
6 x2 = that._x2,
7 y2 = that._y2;
8
9 if (that._l01_a > epsilon) {
10 var a = 2 * that._l01_2a + 3 * that._l01_a * that._l12_a + that._l12_2a,
11 n = 3 * that._l01_a * (that._l01_a + that._l12_a);
12 x1 = (x1 * a - that._x0 * that._l12_2a + that._x2 * that._l01_2a) / n;
13 y1 = (y1 * a - that._y0 * that._l12_2a + that._y2 * that._l01_2a) / n;
14 }
15
16 if (that._l23_a > epsilon) {
17 var b = 2 * that._l23_2a + 3 * that._l23_a * that._l12_a + that._l12_2a,
18 m = 3 * that._l23_a * (that._l23_a + that._l12_a);
19 x2 = (x2 * b + that._x1 * that._l23_2a - x * that._l12_2a) / m;
20 y2 = (y2 * b + that._y1 * that._l23_2a - y * that._l12_2a) / m;
21 }
22
23 that._context.bezierCurveTo(x1, y1, x2, y2, that._x2, that._y2);
24 }
25
26 function CatmullRom(context, alpha) {
27 this._context = context;
28 this._alpha = alpha;
29 }
30
31 CatmullRom.prototype = {
32 areaStart: function() {
33 this._line = 0;
34 },
35 areaEnd: function() {
36 this._line = NaN;
37 },
38 lineStart: function() {
39 this._x0 = this._x1 = this._x2 =
40 this._y0 = this._y1 = this._y2 = NaN;
41 this._l01_a = this._l12_a = this._l23_a =
42 this._l01_2a = this._l12_2a = this._l23_2a =
43 this._point = 0;
44 },
45 lineEnd: function() {
46 switch (this._point) {
47 case 2: this._context.lineTo(this._x2, this._y2); break;
48 case 3: this.point(this._x2, this._y2); break;
49 }
50 if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
51 this._line = 1 - this._line;
52 },
53 point: function(x, y) {
54 x = +x, y = +y;
55
56 if (this._point) {
57 var x23 = this._x2 - x,
58 y23 = this._y2 - y;
59 this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
60 }
61
62 switch (this._point) {
63 case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
64 case 1: this._point = 2; break;
65 case 2: this._point = 3; // proceed
66 default: point(this, x, y); break;
67 }
68
69 this._l01_a = this._l12_a, this._l12_a = this._l23_a;
70 this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
71 this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
72 this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
73 }
74 };
75
76 export default (function custom(alpha) {
77
78 function catmullRom(context) {
79 return alpha ? new CatmullRom(context, alpha) : new Cardinal(context, 0);
80 }
81
82 catmullRom.alpha = function(alpha) {
83 return custom(+alpha);
84 };
85
86 return catmullRom;
87 })(0.5);
0 import {CardinalClosed} from "./cardinalClosed";
1 import noop from "../noop";
2 import {point} from "./catmullRom";
3
4 function CatmullRomClosed(context, alpha) {
5 this._context = context;
6 this._alpha = alpha;
7 }
8
9 CatmullRomClosed.prototype = {
10 areaStart: noop,
11 areaEnd: noop,
12 lineStart: function() {
13 this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =
14 this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;
15 this._l01_a = this._l12_a = this._l23_a =
16 this._l01_2a = this._l12_2a = this._l23_2a =
17 this._point = 0;
18 },
19 lineEnd: function() {
20 switch (this._point) {
21 case 1: {
22 this._context.moveTo(this._x3, this._y3);
23 this._context.closePath();
24 break;
25 }
26 case 2: {
27 this._context.lineTo(this._x3, this._y3);
28 this._context.closePath();
29 break;
30 }
31 case 3: {
32 this.point(this._x3, this._y3);
33 this.point(this._x4, this._y4);
34 this.point(this._x5, this._y5);
35 break;
36 }
37 }
38 },
39 point: function(x, y) {
40 x = +x, y = +y;
41
42 if (this._point) {
43 var x23 = this._x2 - x,
44 y23 = this._y2 - y;
45 this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
46 }
47
48 switch (this._point) {
49 case 0: this._point = 1; this._x3 = x, this._y3 = y; break;
50 case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;
51 case 2: this._point = 3; this._x5 = x, this._y5 = y; break;
52 default: point(this, x, y); break;
53 }
54
55 this._l01_a = this._l12_a, this._l12_a = this._l23_a;
56 this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
57 this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
58 this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
59 }
60 };
61
62 export default (function custom(alpha) {
63
64 function catmullRom(context) {
65 return alpha ? new CatmullRomClosed(context, alpha) : new CardinalClosed(context, 0);
66 }
67
68 catmullRom.alpha = function(alpha) {
69 return custom(+alpha);
70 };
71
72 return catmullRom;
73 })(0.5);
0 import {CardinalOpen} from "./cardinalOpen";
1 import {point} from "./catmullRom";
2
3 function CatmullRomOpen(context, alpha) {
4 this._context = context;
5 this._alpha = alpha;
6 }
7
8 CatmullRomOpen.prototype = {
9 areaStart: function() {
10 this._line = 0;
11 },
12 areaEnd: function() {
13 this._line = NaN;
14 },
15 lineStart: function() {
16 this._x0 = this._x1 = this._x2 =
17 this._y0 = this._y1 = this._y2 = NaN;
18 this._l01_a = this._l12_a = this._l23_a =
19 this._l01_2a = this._l12_2a = this._l23_2a =
20 this._point = 0;
21 },
22 lineEnd: function() {
23 if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
24 this._line = 1 - this._line;
25 },
26 point: function(x, y) {
27 x = +x, y = +y;
28
29 if (this._point) {
30 var x23 = this._x2 - x,
31 y23 = this._y2 - y;
32 this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
33 }
34
35 switch (this._point) {
36 case 0: this._point = 1; break;
37 case 1: this._point = 2; break;
38 case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;
39 case 3: this._point = 4; // proceed
40 default: point(this, x, y); break;
41 }
42
43 this._l01_a = this._l12_a, this._l12_a = this._l23_a;
44 this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
45 this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
46 this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
47 }
48 };
49
50 export default (function custom(alpha) {
51
52 function catmullRom(context) {
53 return alpha ? new CatmullRomOpen(context, alpha) : new CardinalOpen(context, 0);
54 }
55
56 catmullRom.alpha = function(alpha) {
57 return custom(+alpha);
58 };
59
60 return catmullRom;
61 })(0.5);
0 function Linear(context) {
1 this._context = context;
2 }
3
4 Linear.prototype = {
5 areaStart: function() {
6 this._line = 0;
7 },
8 areaEnd: function() {
9 this._line = NaN;
10 },
11 lineStart: function() {
12 this._point = 0;
13 },
14 lineEnd: function() {
15 if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
16 this._line = 1 - this._line;
17 },
18 point: function(x, y) {
19 x = +x, y = +y;
20 switch (this._point) {
21 case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
22 case 1: this._point = 2; // proceed
23 default: this._context.lineTo(x, y); break;
24 }
25 }
26 };
27
28 export default function(context) {
29 return new Linear(context);
30 }
0 import noop from "../noop";
1
2 function LinearClosed(context) {
3 this._context = context;
4 }
5
6 LinearClosed.prototype = {
7 areaStart: noop,
8 areaEnd: noop,
9 lineStart: function() {
10 this._point = 0;
11 },
12 lineEnd: function() {
13 if (this._point) this._context.closePath();
14 },
15 point: function(x, y) {
16 x = +x, y = +y;
17 if (this._point) this._context.lineTo(x, y);
18 else this._point = 1, this._context.moveTo(x, y);
19 }
20 };
21
22 export default function(context) {
23 return new LinearClosed(context);
24 }
0 function sign(x) {
1 return x < 0 ? -1 : 1;
2 }
3
4 // Calculate the slopes of the tangents (Hermite-type interpolation) based on
5 // the following paper: Steffen, M. 1990. A Simple Method for Monotonic
6 // Interpolation in One Dimension. Astronomy and Astrophysics, Vol. 239, NO.
7 // NOV(II), P. 443, 1990.
8 function slope3(that, x2, y2) {
9 var h0 = that._x1 - that._x0,
10 h1 = x2 - that._x1,
11 s0 = (that._y1 - that._y0) / (h0 || h1 < 0 && -0),
12 s1 = (y2 - that._y1) / (h1 || h0 < 0 && -0),
13 p = (s0 * h1 + s1 * h0) / (h0 + h1);
14 return (sign(s0) + sign(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0;
15 }
16
17 // Calculate a one-sided slope.
18 function slope2(that, t) {
19 var h = that._x1 - that._x0;
20 return h ? (3 * (that._y1 - that._y0) / h - t) / 2 : t;
21 }
22
23 // According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations
24 // "you can express cubic Hermite interpolation in terms of cubic Bézier curves
25 // with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1".
26 function point(that, t0, t1) {
27 var x0 = that._x0,
28 y0 = that._y0,
29 x1 = that._x1,
30 y1 = that._y1,
31 dx = (x1 - x0) / 3;
32 that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1);
33 }
34
35 function MonotoneX(context) {
36 this._context = context;
37 }
38
39 MonotoneX.prototype = {
40 areaStart: function() {
41 this._line = 0;
42 },
43 areaEnd: function() {
44 this._line = NaN;
45 },
46 lineStart: function() {
47 this._x0 = this._x1 =
48 this._y0 = this._y1 =
49 this._t0 = NaN;
50 this._point = 0;
51 },
52 lineEnd: function() {
53 switch (this._point) {
54 case 2: this._context.lineTo(this._x1, this._y1); break;
55 case 3: point(this, this._t0, slope2(this, this._t0)); break;
56 }
57 if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
58 this._line = 1 - this._line;
59 },
60 point: function(x, y) {
61 var t1 = NaN;
62
63 x = +x, y = +y;
64 if (x === this._x1 && y === this._y1) return; // Ignore coincident points.
65 switch (this._point) {
66 case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
67 case 1: this._point = 2; break;
68 case 2: this._point = 3; point(this, slope2(this, t1 = slope3(this, x, y)), t1); break;
69 default: point(this, this._t0, t1 = slope3(this, x, y)); break;
70 }
71
72 this._x0 = this._x1, this._x1 = x;
73 this._y0 = this._y1, this._y1 = y;
74 this._t0 = t1;
75 }
76 }
77
78 function MonotoneY(context) {
79 this._context = new ReflectContext(context);
80 }
81
82 (MonotoneY.prototype = Object.create(MonotoneX.prototype)).point = function(x, y) {
83 MonotoneX.prototype.point.call(this, y, x);
84 };
85
86 function ReflectContext(context) {
87 this._context = context;
88 }
89
90 ReflectContext.prototype = {
91 moveTo: function(x, y) { this._context.moveTo(y, x); },
92 closePath: function() { this._context.closePath(); },
93 lineTo: function(x, y) { this._context.lineTo(y, x); },
94 bezierCurveTo: function(x1, y1, x2, y2, x, y) { this._context.bezierCurveTo(y1, x1, y2, x2, y, x); }
95 };
96
97 export function monotoneX(context) {
98 return new MonotoneX(context);
99 }
100
101 export function monotoneY(context) {
102 return new MonotoneY(context);
103 }
0 function Natural(context) {
1 this._context = context;
2 }
3
4 Natural.prototype = {
5 areaStart: function() {
6 this._line = 0;
7 },
8 areaEnd: function() {
9 this._line = NaN;
10 },
11 lineStart: function() {
12 this._x = [];
13 this._y = [];
14 },
15 lineEnd: function() {
16 var x = this._x,
17 y = this._y,
18 n = x.length;
19
20 if (n) {
21 this._line ? this._context.lineTo(x[0], y[0]) : this._context.moveTo(x[0], y[0]);
22 if (n === 2) {
23 this._context.lineTo(x[1], y[1]);
24 } else {
25 var px = controlPoints(x),
26 py = controlPoints(y);
27 for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) {
28 this._context.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]);
29 }
30 }
31 }
32
33 if (this._line || (this._line !== 0 && n === 1)) this._context.closePath();
34 this._line = 1 - this._line;
35 this._x = this._y = null;
36 },
37 point: function(x, y) {
38 this._x.push(+x);
39 this._y.push(+y);
40 }
41 };
42
43 // See https://www.particleincell.com/2012/bezier-splines/ for derivation.
44 function controlPoints(x) {
45 var i,
46 n = x.length - 1,
47 m,
48 a = new Array(n),
49 b = new Array(n),
50 r = new Array(n);
51 a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1];
52 for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1];
53 a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n];
54 for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1];
55 a[n - 1] = r[n - 1] / b[n - 1];
56 for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i];
57 b[n - 1] = (x[n] + a[n - 1]) / 2;
58 for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1];
59 return [a, b];
60 }
61
62 export default function(context) {
63 return new Natural(context);
64 }
0 import curveLinear from "./linear";
1
2 export var curveRadialLinear = curveRadial(curveLinear);
3
4 function Radial(curve) {
5 this._curve = curve;
6 }
7
8 Radial.prototype = {
9 areaStart: function() {
10 this._curve.areaStart();
11 },
12 areaEnd: function() {
13 this._curve.areaEnd();
14 },
15 lineStart: function() {
16 this._curve.lineStart();
17 },
18 lineEnd: function() {
19 this._curve.lineEnd();
20 },
21 point: function(a, r) {
22 this._curve.point(r * Math.sin(a), r * -Math.cos(a));
23 }
24 };
25
26 export default function curveRadial(curve) {
27
28 function radial(context) {
29 return new Radial(curve(context));
30 }
31
32 radial._curve = curve;
33
34 return radial;
35 }
0 function Step(context, t) {
1 this._context = context;
2 this._t = t;
3 }
4
5 Step.prototype = {
6 areaStart: function() {
7 this._line = 0;
8 },
9 areaEnd: function() {
10 this._line = NaN;
11 },
12 lineStart: function() {
13 this._x = this._y = NaN;
14 this._point = 0;
15 },
16 lineEnd: function() {
17 if (0 < this._t && this._t < 1 && this._point === 2) this._context.lineTo(this._x, this._y);
18 if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
19 if (this._line >= 0) this._t = 1 - this._t, this._line = 1 - this._line;
20 },
21 point: function(x, y) {
22 x = +x, y = +y;
23 switch (this._point) {
24 case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
25 case 1: this._point = 2; // proceed
26 default: {
27 if (this._t <= 0) {
28 this._context.lineTo(this._x, y);
29 this._context.lineTo(x, y);
30 } else {
31 var x1 = this._x * (1 - this._t) + x * this._t;
32 this._context.lineTo(x1, this._y);
33 this._context.lineTo(x1, y);
34 }
35 break;
36 }
37 }
38 this._x = x, this._y = y;
39 }
40 };
41
42 export default function(context) {
43 return new Step(context, 0.5);
44 }
45
46 export function stepBefore(context) {
47 return new Step(context, 0);
48 }
49
50 export function stepAfter(context) {
51 return new Step(context, 1);
52 }
0 export default function(a, b) {
1 return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
2 }
0 export default function(d) {
1 return d;
2 }
0 import {path} from "d3-path";
1 import constant from "./constant";
2 import curveLinear from "./curve/linear";
3 import {x as pointX, y as pointY} from "./point";
4
5 export default function() {
6 var x = pointX,
7 y = pointY,
8 defined = constant(true),
9 context = null,
10 curve = curveLinear,
11 output = null;
12
13 function line(data) {
14 var i,
15 n = data.length,
16 d,
17 defined0 = false,
18 buffer;
19
20 if (context == null) output = curve(buffer = path());
21
22 for (i = 0; i <= n; ++i) {
23 if (!(i < n && defined(d = data[i], i, data)) === defined0) {
24 if (defined0 = !defined0) output.lineStart();
25 else output.lineEnd();
26 }
27 if (defined0) output.point(+x(d, i, data), +y(d, i, data));
28 }
29
30 if (buffer) return output = null, buffer + "" || null;
31 }
32
33 line.x = function(_) {
34 return arguments.length ? (x = typeof _ === "function" ? _ : constant(+_), line) : x;
35 };
36
37 line.y = function(_) {
38 return arguments.length ? (y = typeof _ === "function" ? _ : constant(+_), line) : y;
39 };
40
41 line.defined = function(_) {
42 return arguments.length ? (defined = typeof _ === "function" ? _ : constant(!!_), line) : defined;
43 };
44
45 line.curve = function(_) {
46 return arguments.length ? (curve = _, context != null && (output = curve(context)), line) : curve;
47 };
48
49 line.context = function(_) {
50 return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context;
51 };
52
53 return line;
54 }
0 import curveRadial, {curveRadialLinear} from "./curve/radial";
1 import line from "./line";
2
3 export function lineRadial(l) {
4 var c = l.curve;
5
6 l.angle = l.x, delete l.x;
7 l.radius = l.y, delete l.y;
8
9 l.curve = function(_) {
10 return arguments.length ? c(curveRadial(_)) : c()._curve;
11 };
12
13 return l;
14 }
15
16 export default function() {
17 return lineRadial(line().curve(curveRadialLinear));
18 }
0 import {path} from "d3-path";
1 import {slice} from "../array";
2 import constant from "../constant";
3 import {x as pointX, y as pointY} from "../point";
4 import pointRadial from "../pointRadial";
5
6 function linkSource(d) {
7 return d.source;
8 }
9
10 function linkTarget(d) {
11 return d.target;
12 }
13
14 function link(curve) {
15 var source = linkSource,
16 target = linkTarget,
17 x = pointX,
18 y = pointY,
19 context = null;
20
21 function link() {
22 var buffer, argv = slice.call(arguments), s = source.apply(this, argv), t = target.apply(this, argv);
23 if (!context) context = buffer = path();
24 curve(context, +x.apply(this, (argv[0] = s, argv)), +y.apply(this, argv), +x.apply(this, (argv[0] = t, argv)), +y.apply(this, argv));
25 if (buffer) return context = null, buffer + "" || null;
26 }
27
28 link.source = function(_) {
29 return arguments.length ? (source = _, link) : source;
30 };
31
32 link.target = function(_) {
33 return arguments.length ? (target = _, link) : target;
34 };
35
36 link.x = function(_) {
37 return arguments.length ? (x = typeof _ === "function" ? _ : constant(+_), link) : x;
38 };
39
40 link.y = function(_) {
41 return arguments.length ? (y = typeof _ === "function" ? _ : constant(+_), link) : y;
42 };
43
44 link.context = function(_) {
45 return arguments.length ? ((context = _ == null ? null : _), link) : context;
46 };
47
48 return link;
49 }
50
51 function curveHorizontal(context, x0, y0, x1, y1) {
52 context.moveTo(x0, y0);
53 context.bezierCurveTo(x0 = (x0 + x1) / 2, y0, x0, y1, x1, y1);
54 }
55
56 function curveVertical(context, x0, y0, x1, y1) {
57 context.moveTo(x0, y0);
58 context.bezierCurveTo(x0, y0 = (y0 + y1) / 2, x1, y0, x1, y1);
59 }
60
61 function curveRadial(context, x0, y0, x1, y1) {
62 var p0 = pointRadial(x0, y0),
63 p1 = pointRadial(x0, y0 = (y0 + y1) / 2),
64 p2 = pointRadial(x1, y0),
65 p3 = pointRadial(x1, y1);
66 context.moveTo(p0[0], p0[1]);
67 context.bezierCurveTo(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]);
68 }
69
70 export function linkHorizontal() {
71 return link(curveHorizontal);
72 }
73
74 export function linkVertical() {
75 return link(curveVertical);
76 }
77
78 export function linkRadial() {
79 var l = link(curveRadial);
80 l.angle = l.x, delete l.x;
81 l.radius = l.y, delete l.y;
82 return l;
83 }
0 export var abs = Math.abs;
1 export var atan2 = Math.atan2;
2 export var cos = Math.cos;
3 export var max = Math.max;
4 export var min = Math.min;
5 export var sin = Math.sin;
6 export var sqrt = Math.sqrt;
7
8 export var epsilon = 1e-12;
9 export var pi = Math.PI;
10 export var halfPi = pi / 2;
11 export var tau = 2 * pi;
12
13 export function acos(x) {
14 return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);
15 }
16
17 export function asin(x) {
18 return x >= 1 ? halfPi : x <= -1 ? -halfPi : Math.asin(x);
19 }
0 export default function() {}
0 export default function(series, order) {
1 if (!((n = series.length) > 1)) return;
2 for (var i, j = 0, d, dy, yp, yn, n, m = series[order[0]].length; j < m; ++j) {
3 for (yp = yn = 0, i = 0; i < n; ++i) {
4 if ((dy = (d = series[order[i]][j])[1] - d[0]) >= 0) {
5 d[0] = yp, d[1] = yp += dy;
6 } else if (dy < 0) {
7 d[1] = yn, d[0] = yn += dy;
8 } else {
9 d[0] = yp;
10 }
11 }
12 }
13 }
0 import none from "./none";
1
2 export default function(series, order) {
3 if (!((n = series.length) > 0)) return;
4 for (var i, n, j = 0, m = series[0].length, y; j < m; ++j) {
5 for (y = i = 0; i < n; ++i) y += series[i][j][1] || 0;
6 if (y) for (i = 0; i < n; ++i) series[i][j][1] /= y;
7 }
8 none(series, order);
9 }
0 export default function(series, order) {
1 if (!((n = series.length) > 1)) return;
2 for (var i = 1, j, s0, s1 = series[order[0]], n, m = s1.length; i < n; ++i) {
3 s0 = s1, s1 = series[order[i]];
4 for (j = 0; j < m; ++j) {
5 s1[j][1] += s1[j][0] = isNaN(s0[j][1]) ? s0[j][0] : s0[j][1];
6 }
7 }
8 }
0 import none from "./none";
1
2 export default function(series, order) {
3 if (!((n = series.length) > 0)) return;
4 for (var j = 0, s0 = series[order[0]], n, m = s0.length; j < m; ++j) {
5 for (var i = 0, y = 0; i < n; ++i) y += series[i][j][1] || 0;
6 s0[j][1] += s0[j][0] = -y / 2;
7 }
8 none(series, order);
9 }
0 import none from "./none";
1
2 export default function(series, order) {
3 if (!((n = series.length) > 0) || !((m = (s0 = series[order[0]]).length) > 0)) return;
4 for (var y = 0, j = 1, s0, m, n; j < m; ++j) {
5 for (var i = 0, s1 = 0, s2 = 0; i < n; ++i) {
6 var si = series[order[i]],
7 sij0 = si[j][1] || 0,
8 sij1 = si[j - 1][1] || 0,
9 s3 = (sij0 - sij1) / 2;
10 for (var k = 0; k < i; ++k) {
11 var sk = series[order[k]],
12 skj0 = sk[j][1] || 0,
13 skj1 = sk[j - 1][1] || 0;
14 s3 += skj0 - skj1;
15 }
16 s1 += sij0, s2 += s3 * sij0;
17 }
18 s0[j - 1][1] += s0[j - 1][0] = y;
19 if (s1) y -= s2 / s1;
20 }
21 s0[j - 1][1] += s0[j - 1][0] = y;
22 none(series, order);
23 }
0 import none from "./none";
1
2 export default function(series) {
3 var sums = series.map(sum);
4 return none(series).sort(function(a, b) { return sums[a] - sums[b]; });
5 }
6
7 export function sum(series) {
8 var s = 0, i = -1, n = series.length, v;
9 while (++i < n) if (v = +series[i][1]) s += v;
10 return s;
11 }
0 import ascending from "./ascending";
1
2 export default function(series) {
3 return ascending(series).reverse();
4 }
0 import none from "./none";
1 import {sum} from "./ascending";
2
3 export default function(series) {
4 var n = series.length,
5 i,
6 j,
7 sums = series.map(sum),
8 order = none(series).sort(function(a, b) { return sums[b] - sums[a]; }),
9 top = 0,
10 bottom = 0,
11 tops = [],
12 bottoms = [];
13
14 for (i = 0; i < n; ++i) {
15 j = order[i];
16 if (top < bottom) {
17 top += sums[j];
18 tops.push(j);
19 } else {
20 bottom += sums[j];
21 bottoms.push(j);
22 }
23 }
24
25 return bottoms.reverse().concat(tops);
26 }
0 export default function(series) {
1 var n = series.length, o = new Array(n);
2 while (--n >= 0) o[n] = n;
3 return o;
4 }
0 import none from "./none";
1
2 export default function(series) {
3 return none(series).reverse();
4 }
0 import constant from "./constant";
1 import descending from "./descending";
2 import identity from "./identity";
3 import {tau} from "./math";
4
5 export default function() {
6 var value = identity,
7 sortValues = descending,
8 sort = null,
9 startAngle = constant(0),
10 endAngle = constant(tau),
11 padAngle = constant(0);
12
13 function pie(data) {
14 var i,
15 n = data.length,
16 j,
17 k,
18 sum = 0,
19 index = new Array(n),
20 arcs = new Array(n),
21 a0 = +startAngle.apply(this, arguments),
22 da = Math.min(tau, Math.max(-tau, endAngle.apply(this, arguments) - a0)),
23 a1,
24 p = Math.min(Math.abs(da) / n, padAngle.apply(this, arguments)),
25 pa = p * (da < 0 ? -1 : 1),
26 v;
27
28 for (i = 0; i < n; ++i) {
29 if ((v = arcs[index[i] = i] = +value(data[i], i, data)) > 0) {
30 sum += v;
31 }
32 }
33
34 // Optionally sort the arcs by previously-computed values or by data.
35 if (sortValues != null) index.sort(function(i, j) { return sortValues(arcs[i], arcs[j]); });
36 else if (sort != null) index.sort(function(i, j) { return sort(data[i], data[j]); });
37
38 // Compute the arcs! They are stored in the original data's order.
39 for (i = 0, k = sum ? (da - n * pa) / sum : 0; i < n; ++i, a0 = a1) {
40 j = index[i], v = arcs[j], a1 = a0 + (v > 0 ? v * k : 0) + pa, arcs[j] = {
41 data: data[j],
42 index: i,
43 value: v,
44 startAngle: a0,
45 endAngle: a1,
46 padAngle: p
47 };
48 }
49
50 return arcs;
51 }
52
53 pie.value = function(_) {
54 return arguments.length ? (value = typeof _ === "function" ? _ : constant(+_), pie) : value;
55 };
56
57 pie.sortValues = function(_) {
58 return arguments.length ? (sortValues = _, sort = null, pie) : sortValues;
59 };
60
61 pie.sort = function(_) {
62 return arguments.length ? (sort = _, sortValues = null, pie) : sort;
63 };
64
65 pie.startAngle = function(_) {
66 return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant(+_), pie) : startAngle;
67 };
68
69 pie.endAngle = function(_) {
70 return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant(+_), pie) : endAngle;
71 };
72
73 pie.padAngle = function(_) {
74 return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant(+_), pie) : padAngle;
75 };
76
77 return pie;
78 }
0 export function x(p) {
1 return p[0];
2 }
3
4 export function y(p) {
5 return p[1];
6 }
0 export default function(x, y) {
1 return [(y = +y) * Math.cos(x -= Math.PI / 2), y * Math.sin(x)];
2 }
0 import {slice} from "./array";
1 import constant from "./constant";
2 import offsetNone from "./offset/none";
3 import orderNone from "./order/none";
4
5 function stackValue(d, key) {
6 return d[key];
7 }
8
9 export default function() {
10 var keys = constant([]),
11 order = orderNone,
12 offset = offsetNone,
13 value = stackValue;
14
15 function stack(data) {
16 var kz = keys.apply(this, arguments),
17 i,
18 m = data.length,
19 n = kz.length,
20 sz = new Array(n),
21 oz;
22
23 for (i = 0; i < n; ++i) {
24 for (var ki = kz[i], si = sz[i] = new Array(m), j = 0, sij; j < m; ++j) {
25 si[j] = sij = [0, +value(data[j], ki, j, data)];
26 sij.data = data[j];
27 }
28 si.key = ki;
29 }
30
31 for (i = 0, oz = order(sz); i < n; ++i) {
32 sz[oz[i]].index = i;
33 }
34
35 offset(sz, oz);
36 return sz;
37 }
38
39 stack.keys = function(_) {
40 return arguments.length ? (keys = typeof _ === "function" ? _ : constant(slice.call(_)), stack) : keys;
41 };
42
43 stack.value = function(_) {
44 return arguments.length ? (value = typeof _ === "function" ? _ : constant(+_), stack) : value;
45 };
46
47 stack.order = function(_) {
48 return arguments.length ? (order = _ == null ? orderNone : typeof _ === "function" ? _ : constant(slice.call(_)), stack) : order;
49 };
50
51 stack.offset = function(_) {
52 return arguments.length ? (offset = _ == null ? offsetNone : _, stack) : offset;
53 };
54
55 return stack;
56 }
0 import {pi, tau} from "../math";
1
2 export default {
3 draw: function(context, size) {
4 var r = Math.sqrt(size / pi);
5 context.moveTo(r, 0);
6 context.arc(0, 0, r, 0, tau);
7 }
8 };
0 export default {
1 draw: function(context, size) {
2 var r = Math.sqrt(size / 5) / 2;
3 context.moveTo(-3 * r, -r);
4 context.lineTo(-r, -r);
5 context.lineTo(-r, -3 * r);
6 context.lineTo(r, -3 * r);
7 context.lineTo(r, -r);
8 context.lineTo(3 * r, -r);
9 context.lineTo(3 * r, r);
10 context.lineTo(r, r);
11 context.lineTo(r, 3 * r);
12 context.lineTo(-r, 3 * r);
13 context.lineTo(-r, r);
14 context.lineTo(-3 * r, r);
15 context.closePath();
16 }
17 };
0 var tan30 = Math.sqrt(1 / 3),
1 tan30_2 = tan30 * 2;
2
3 export default {
4 draw: function(context, size) {
5 var y = Math.sqrt(size / tan30_2),
6 x = y * tan30;
7 context.moveTo(0, -y);
8 context.lineTo(x, 0);
9 context.lineTo(0, y);
10 context.lineTo(-x, 0);
11 context.closePath();
12 }
13 };
0 export default {
1 draw: function(context, size) {
2 var w = Math.sqrt(size),
3 x = -w / 2;
4 context.rect(x, x, w, w);
5 }
6 };
0 import {pi, tau} from "../math";
1
2 var ka = 0.89081309152928522810,
3 kr = Math.sin(pi / 10) / Math.sin(7 * pi / 10),
4 kx = Math.sin(tau / 10) * kr,
5 ky = -Math.cos(tau / 10) * kr;
6
7 export default {
8 draw: function(context, size) {
9 var r = Math.sqrt(size * ka),
10 x = kx * r,
11 y = ky * r;
12 context.moveTo(0, -r);
13 context.lineTo(x, y);
14 for (var i = 1; i < 5; ++i) {
15 var a = tau * i / 5,
16 c = Math.cos(a),
17 s = Math.sin(a);
18 context.lineTo(s * r, -c * r);
19 context.lineTo(c * x - s * y, s * x + c * y);
20 }
21 context.closePath();
22 }
23 };
0 var sqrt3 = Math.sqrt(3);
1
2 export default {
3 draw: function(context, size) {
4 var y = -Math.sqrt(size / (sqrt3 * 3));
5 context.moveTo(0, y * 2);
6 context.lineTo(-sqrt3 * y, -y);
7 context.lineTo(sqrt3 * y, -y);
8 context.closePath();
9 }
10 };
0 var c = -0.5,
1 s = Math.sqrt(3) / 2,
2 k = 1 / Math.sqrt(12),
3 a = (k / 2 + 1) * 3;
4
5 export default {
6 draw: function(context, size) {
7 var r = Math.sqrt(size / a),
8 x0 = r / 2,
9 y0 = r * k,
10 x1 = x0,
11 y1 = r * k + r,
12 x2 = -x1,
13 y2 = y1;
14 context.moveTo(x0, y0);
15 context.lineTo(x1, y1);
16 context.lineTo(x2, y2);
17 context.lineTo(c * x0 - s * y0, s * x0 + c * y0);
18 context.lineTo(c * x1 - s * y1, s * x1 + c * y1);
19 context.lineTo(c * x2 - s * y2, s * x2 + c * y2);
20 context.lineTo(c * x0 + s * y0, c * y0 - s * x0);
21 context.lineTo(c * x1 + s * y1, c * y1 - s * x1);
22 context.lineTo(c * x2 + s * y2, c * y2 - s * x2);
23 context.closePath();
24 }
25 };
0 import {path} from "d3-path";
1 import circle from "./symbol/circle";
2 import cross from "./symbol/cross";
3 import diamond from "./symbol/diamond";
4 import star from "./symbol/star";
5 import square from "./symbol/square";
6 import triangle from "./symbol/triangle";
7 import wye from "./symbol/wye";
8 import constant from "./constant";
9
10 export var symbols = [
11 circle,
12 cross,
13 diamond,
14 square,
15 star,
16 triangle,
17 wye
18 ];
19
20 export default function() {
21 var type = constant(circle),
22 size = constant(64),
23 context = null;
24
25 function symbol() {
26 var buffer;
27 if (!context) context = buffer = path();
28 type.apply(this, arguments).draw(context, +size.apply(this, arguments));
29 if (buffer) return context = null, buffer + "" || null;
30 }
31
32 symbol.type = function(_) {
33 return arguments.length ? (type = typeof _ === "function" ? _ : constant(_), symbol) : type;
34 };
35
36 symbol.size = function(_) {
37 return arguments.length ? (size = typeof _ === "function" ? _ : constant(+_), symbol) : size;
38 };
39
40 symbol.context = function(_) {
41 return arguments.length ? (context = _ == null ? null : _, symbol) : context;
42 };
43
44 return symbol;
45 }
0 var tape = require("tape"),
1 shape = require("../");
2
3 require("./pathEqual");
4
5 tape("arc().innerRadius(f)(…) propagates the context and arguments to the specified function f", function(test) {
6 var expected = {that: {}, args: [42]}, actual;
7 shape.arc().innerRadius(function() { actual = {that: this, args: [].slice.call(arguments)}; }).apply(expected.that, expected.args);
8 test.deepEqual(actual, expected);
9 test.end();
10 });
11
12 tape("arc().outerRadius(f)(…) propagates the context and arguments to the specified function f", function(test) {
13 var expected = {that: {}, args: [42]}, actual;
14 shape.arc().outerRadius(function() { actual = {that: this, args: [].slice.call(arguments)}; }).apply(expected.that, expected.args);
15 test.deepEqual(actual, expected);
16 test.end();
17 });
18
19 tape("arc().cornerRadius(f)(…) propagates the context and arguments to the specified function f", function(test) {
20 var expected = {that: {}, args: [42]}, actual;
21 shape.arc().outerRadius(100).cornerRadius(function() { actual = {that: this, args: [].slice.call(arguments)}; }).apply(expected.that, expected.args);
22 test.deepEqual(actual, expected);
23 test.end();
24 });
25
26 tape("arc().startAngle(f)(…) propagates the context and arguments to the specified function f", function(test) {
27 var expected = {that: {}, args: [42]}, actual;
28 shape.arc().startAngle(function() { actual = {that: this, args: [].slice.call(arguments)}; }).apply(expected.that, expected.args);
29 test.deepEqual(actual, expected);
30 test.end();
31 });
32
33 tape("arc().endAngle(f)(…) propagates the context and arguments to the specified function f", function(test) {
34 var expected = {that: {}, args: [42]}, actual;
35 shape.arc().endAngle(function() { actual = {that: this, args: [].slice.call(arguments)}; }).apply(expected.that, expected.args);
36 test.deepEqual(actual, expected);
37 test.end();
38 });
39
40 tape("arc().padAngle(f)(…) propagates the context and arguments to the specified function f", function(test) {
41 var expected = {that: {}, args: [42]}, actual;
42 shape.arc().outerRadius(100).startAngle(Math.PI / 2).padAngle(function() { actual = {that: this, args: [].slice.call(arguments)}; }).apply(expected.that, expected.args);
43 test.deepEqual(actual, expected);
44 test.end();
45 });
46
47 tape("arc().padRadius(f)(…) propagates the context and arguments to the specified function f", function(test) {
48 var expected = {that: {}, args: [42]}, actual;
49 shape.arc().outerRadius(100).startAngle(Math.PI / 2).padAngle(0.1).padRadius(function() { actual = {that: this, args: [].slice.call(arguments)}; }).apply(expected.that, expected.args);
50 test.deepEqual(actual, expected);
51 test.end();
52 });
53
54 tape("arc().centroid(…) computes the midpoint of the center line of the arc", function(test) {
55 var a = shape.arc(), round = function(x) { return Math.round(x * 1e6) / 1e6; };
56 test.deepEqual(a.centroid({innerRadius: 0, outerRadius: 100, startAngle: 0, endAngle: Math.PI}).map(round), [50, 0]);
57 test.deepEqual(a.centroid({innerRadius: 0, outerRadius: 100, startAngle: 0, endAngle: Math.PI / 2}).map(round), [35.355339, -35.355339]);
58 test.deepEqual(a.centroid({innerRadius: 50, outerRadius: 100, startAngle: 0, endAngle: -Math.PI}).map(round), [-75, 0]);
59 test.deepEqual(a.centroid({innerRadius: 50, outerRadius: 100, startAngle: 0, endAngle: -Math.PI / 2}).map(round), [-53.033009, -53.033009]);
60 test.end();
61 });
62
63 tape("arc().innerRadius(f).centroid(…) propagates the context and arguments to the specified function f", function(test) {
64 var expected = {that: {}, args: [42]}, actual;
65 shape.arc().innerRadius(function() { actual = {that: this, args: [].slice.call(arguments)}; }).centroid.apply(expected.that, expected.args);
66 test.deepEqual(actual, expected);
67 test.end();
68 });
69
70 tape("arc().outerRadius(f).centroid(…) propagates the context and arguments to the specified function f", function(test) {
71 var expected = {that: {}, args: [42]}, actual;
72 shape.arc().outerRadius(function() { actual = {that: this, args: [].slice.call(arguments)}; }).centroid.apply(expected.that, expected.args);
73 test.deepEqual(actual, expected);
74 test.end();
75 });
76
77 tape("arc().startAngle(f).centroid(…) propagates the context and arguments to the specified function f", function(test) {
78 var expected = {that: {}, args: [42]}, actual;
79 shape.arc().startAngle(function() { actual = {that: this, args: [].slice.call(arguments)}; }).centroid.apply(expected.that, expected.args);
80 test.deepEqual(actual, expected);
81 test.end();
82 });
83
84 tape("arc().endAngle(f).centroid(…) propagates the context and arguments to the specified function f", function(test) {
85 var expected = {that: {}, args: [42]}, actual;
86 shape.arc().endAngle(function() { actual = {that: this, args: [].slice.call(arguments)}; }).centroid.apply(expected.that, expected.args);
87 test.deepEqual(actual, expected);
88 test.end();
89 });
90
91 tape("arc().innerRadius(0).outerRadius(0) renders a point", function(test) {
92 var a = shape.arc().innerRadius(0).outerRadius(0);
93 test.pathEqual(a.startAngle(0).endAngle(2 * Math.PI)(), "M0,0Z");
94 test.pathEqual(a.startAngle(0).endAngle(0)(), "M0,0Z");
95 test.end();
96 });
97
98 tape("arc().innerRadius(0).outerRadius(r).startAngle(θ₀).endAngle(θ₁) renders a clockwise circle if r > 0 and θ₁ - θ₀ ≥ τ", function(test) {
99 var a = shape.arc().innerRadius(0).outerRadius(100);
100 test.pathEqual(a.startAngle(0).endAngle(2 * Math.PI)(), "M0,-100A100,100,0,1,1,0,100A100,100,0,1,1,0,-100Z");
101 test.pathEqual(a.startAngle(0).endAngle(3 * Math.PI)(), "M0,-100A100,100,0,1,1,0,100A100,100,0,1,1,0,-100Z");
102 test.pathEqual(a.startAngle(-2 * Math.PI).endAngle(0)(), "M0,-100A100,100,0,1,1,0,100A100,100,0,1,1,0,-100Z");
103 test.pathEqual(a.startAngle(-Math.PI).endAngle(Math.PI)(), "M0,100A100,100,0,1,1,0,-100A100,100,0,1,1,0,100Z");
104 test.pathEqual(a.startAngle(-3 * Math.PI).endAngle(0)(), "M0,100A100,100,0,1,1,0,-100A100,100,0,1,1,0,100Z");
105 test.end();
106 });
107
108 tape("arc().innerRadius(0).outerRadius(r).startAngle(θ₀).endAngle(θ₁) renders an anticlockwise circle if r > 0 and θ₀ - θ₁ ≥ τ", function(test) {
109 var a = shape.arc().innerRadius(0).outerRadius(100);
110 test.pathEqual(a.startAngle(0).endAngle(-2 * Math.PI)(), "M0,-100A100,100,0,1,0,0,100A100,100,0,1,0,0,-100Z");
111 test.pathEqual(a.startAngle(0).endAngle(-3 * Math.PI)(), "M0,-100A100,100,0,1,0,0,100A100,100,0,1,0,0,-100Z");
112 test.pathEqual(a.startAngle(2 * Math.PI).endAngle(0)(), "M0,-100A100,100,0,1,0,0,100A100,100,0,1,0,0,-100Z");
113 test.pathEqual(a.startAngle(Math.PI).endAngle(-Math.PI)(), "M0,100A100,100,0,1,0,0,-100A100,100,0,1,0,0,100Z");
114 test.pathEqual(a.startAngle(3 * Math.PI).endAngle(0)(), "M0,100A100,100,0,1,0,0,-100A100,100,0,1,0,0,100Z");
115 test.end();
116 });
117
118 // Note: The outer ring starts and ends at θ₀, but the inner ring starts and ends at θ₁.
119 // Note: The outer ring is clockwise, but the inner ring is anticlockwise.
120 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(θ₀).endAngle(θ₁) renders a clockwise annulus if r₀ > 0, r₁ > 0 and θ₀ - θ₁ ≥ τ", function(test) {
121 var a = shape.arc().innerRadius(50).outerRadius(100);
122 test.pathEqual(a.startAngle(0).endAngle(2 * Math.PI)(), "M0,-100A100,100,0,1,1,0,100A100,100,0,1,1,0,-100M0,-50A50,50,0,1,0,0,50A50,50,0,1,0,0,-50Z");
123 test.pathEqual(a.startAngle(0).endAngle(3 * Math.PI)(), "M0,-100A100,100,0,1,1,0,100A100,100,0,1,1,0,-100M0,50A50,50,0,1,0,0,-50A50,50,0,1,0,0,50Z");
124 test.pathEqual(a.startAngle(-2 * Math.PI).endAngle(0)(), "M0,-100A100,100,0,1,1,0,100A100,100,0,1,1,0,-100M0,-50A50,50,0,1,0,0,50A50,50,0,1,0,0,-50Z");
125 test.pathEqual(a.startAngle(-Math.PI).endAngle(Math.PI)(), "M0,100A100,100,0,1,1,0,-100A100,100,0,1,1,0,100M0,50A50,50,0,1,0,0,-50A50,50,0,1,0,0,50Z");
126 test.pathEqual(a.startAngle(-3 * Math.PI).endAngle(0)(), "M0,100A100,100,0,1,1,0,-100A100,100,0,1,1,0,100M0,-50A50,50,0,1,0,0,50A50,50,0,1,0,0,-50Z");
127 test.end();
128 });
129
130 // Note: The outer ring starts and ends at θ₀, but the inner ring starts and ends at θ₁.
131 // Note: The outer ring is anticlockwise, but the inner ring is clockwise.
132 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(θ₀).endAngle(θ₁) renders an anticlockwise annulus if r₀ > 0, r₁ > 0 and θ₁ - θ₀ ≥ τ", function(test) {
133 var a = shape.arc().innerRadius(50).outerRadius(100);
134 test.pathEqual(a.startAngle(0).endAngle(-2 * Math.PI)(), "M0,-100A100,100,0,1,0,0,100A100,100,0,1,0,0,-100M0,-50A50,50,0,1,1,0,50A50,50,0,1,1,0,-50Z");
135 test.pathEqual(a.startAngle(0).endAngle(-3 * Math.PI)(), "M0,-100A100,100,0,1,0,0,100A100,100,0,1,0,0,-100M0,50A50,50,0,1,1,0,-50A50,50,0,1,1,0,50Z");
136 test.pathEqual(a.startAngle(2 * Math.PI).endAngle(0)(), "M0,-100A100,100,0,1,0,0,100A100,100,0,1,0,0,-100M0,-50A50,50,0,1,1,0,50A50,50,0,1,1,0,-50Z");
137 test.pathEqual(a.startAngle(Math.PI).endAngle(-Math.PI)(), "M0,100A100,100,0,1,0,0,-100A100,100,0,1,0,0,100M0,50A50,50,0,1,1,0,-50A50,50,0,1,1,0,50Z");
138 test.pathEqual(a.startAngle(3 * Math.PI).endAngle(0)(), "M0,100A100,100,0,1,0,0,-100A100,100,0,1,0,0,100M0,-50A50,50,0,1,1,0,50A50,50,0,1,1,0,-50Z");
139 test.end();
140 });
141
142 tape("arc().innerRadius(0).outerRadius(r).startAngle(θ₀).endAngle(θ₁) renders a small clockwise sector if r > 0 and π > θ₁ - θ₀ ≥ 0", function(test) {
143 var a = shape.arc().innerRadius(0).outerRadius(100);
144 test.pathEqual(a.startAngle(0).endAngle(Math.PI / 2)(), "M0,-100A100,100,0,0,1,100,0L0,0Z");
145 test.pathEqual(a.startAngle(2 * Math.PI).endAngle(5 * Math.PI / 2)(), "M0,-100A100,100,0,0,1,100,0L0,0Z");
146 test.pathEqual(a.startAngle(-Math.PI).endAngle(-Math.PI / 2)(), "M0,100A100,100,0,0,1,-100,0L0,0Z");
147 test.end();
148 });
149
150 tape("arc().innerRadius(0).outerRadius(r).startAngle(θ₀).endAngle(θ₁) renders a small anticlockwise sector if r > 0 and π > θ₀ - θ₁ ≥ 0", function(test) {
151 var a = shape.arc().innerRadius(0).outerRadius(100);
152 test.pathEqual(a.startAngle(0).endAngle(-Math.PI / 2)(), "M0,-100A100,100,0,0,0,-100,0L0,0Z");
153 test.pathEqual(a.startAngle(-2 * Math.PI).endAngle(-5 * Math.PI / 2)(), "M0,-100A100,100,0,0,0,-100,0L0,0Z");
154 test.pathEqual(a.startAngle(Math.PI).endAngle(Math.PI / 2)(), "M0,100A100,100,0,0,0,100,0L0,0Z");
155 test.end();
156 });
157
158 tape("arc().innerRadius(0).outerRadius(r).startAngle(θ₀).endAngle(θ₁) renders a large clockwise sector if r > 0 and τ > θ₁ - θ₀ ≥ π", function(test) {
159 var a = shape.arc().innerRadius(0).outerRadius(100);
160 test.pathEqual(a.startAngle(0).endAngle(3 * Math.PI / 2)(), "M0,-100A100,100,0,1,1,-100,0L0,0Z");
161 test.pathEqual(a.startAngle(2 * Math.PI).endAngle(7 * Math.PI / 2)(), "M0,-100A100,100,0,1,1,-100,0L0,0Z");
162 test.pathEqual(a.startAngle(-Math.PI).endAngle(Math.PI / 2)(), "M0,100A100,100,0,1,1,100,0L0,0Z");
163 test.end();
164 });
165
166 tape("arc().innerRadius(0).outerRadius(r).startAngle(θ₀).endAngle(θ₁) renders a large anticlockwise sector if r > 0 and τ > θ₀ - θ₁ ≥ π", function(test) {
167 var a = shape.arc().innerRadius(0).outerRadius(100);
168 test.pathEqual(a.startAngle(0).endAngle(-3 * Math.PI / 2)(), "M0,-100A100,100,0,1,0,100,0L0,0Z");
169 test.pathEqual(a.startAngle(-2 * Math.PI).endAngle(-7 * Math.PI / 2)(), "M0,-100A100,100,0,1,0,100,0L0,0Z");
170 test.pathEqual(a.startAngle(Math.PI).endAngle(-Math.PI / 2)(), "M0,100A100,100,0,1,0,-100,0L0,0Z");
171 test.end();
172 });
173
174 // Note: The outer ring is clockwise, but the inner ring is anticlockwise.
175 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(θ₀).endAngle(θ₁) renders a small clockwise annular sector if r₀ > 0, r₁ > 0 and π > θ₁ - θ₀ ≥ 0", function(test) {
176 var a = shape.arc().innerRadius(50).outerRadius(100);
177 test.pathEqual(a.startAngle(0).endAngle(Math.PI / 2)(), "M0,-100A100,100,0,0,1,100,0L50,0A50,50,0,0,0,0,-50Z");
178 test.pathEqual(a.startAngle(2 * Math.PI).endAngle(5 * Math.PI / 2)(), "M0,-100A100,100,0,0,1,100,0L50,0A50,50,0,0,0,0,-50Z");
179 test.pathEqual(a.startAngle(-Math.PI).endAngle(-Math.PI / 2)(), "M0,100A100,100,0,0,1,-100,0L-50,0A50,50,0,0,0,0,50Z");
180 test.end();
181 });
182
183 // Note: The outer ring is anticlockwise, but the inner ring is clockwise.
184 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(θ₀).endAngle(θ₁) renders a small anticlockwise annular sector if r₀ > 0, r₁ > 0 and π > θ₀ - θ₁ ≥ 0", function(test) {
185 var a = shape.arc().innerRadius(50).outerRadius(100);
186 test.pathEqual(a.startAngle(0).endAngle(-Math.PI / 2)(), "M0,-100A100,100,0,0,0,-100,0L-50,0A50,50,0,0,1,0,-50Z");
187 test.pathEqual(a.startAngle(-2 * Math.PI).endAngle(-5 * Math.PI / 2)(), "M0,-100A100,100,0,0,0,-100,0L-50,0A50,50,0,0,1,0,-50Z");
188 test.pathEqual(a.startAngle(Math.PI).endAngle(Math.PI / 2)(), "M0,100A100,100,0,0,0,100,0L50,0A50,50,0,0,1,0,50Z");
189 test.end();
190 });
191
192 // Note: The outer ring is clockwise, but the inner ring is anticlockwise.
193 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(θ₀).endAngle(θ₁) renders a large clockwise annular sector if r₀ > 0, r₁ > 0 and τ > θ₁ - θ₀ ≥ π", function(test) {
194 var a = shape.arc().innerRadius(50).outerRadius(100);
195 test.pathEqual(a.startAngle(0).endAngle(3 * Math.PI / 2)(), "M0,-100A100,100,0,1,1,-100,0L-50,0A50,50,0,1,0,0,-50Z");
196 test.pathEqual(a.startAngle(2 * Math.PI).endAngle(7 * Math.PI / 2)(), "M0,-100A100,100,0,1,1,-100,0L-50,0A50,50,0,1,0,0,-50Z");
197 test.pathEqual(a.startAngle(-Math.PI).endAngle(Math.PI / 2)(), "M0,100A100,100,0,1,1,100,0L50,0A50,50,0,1,0,0,50Z");
198 test.end();
199 });
200
201 // Note: The outer ring is anticlockwise, but the inner ring is clockwise.
202 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(θ₀).endAngle(θ₁) renders a large anticlockwise annular sector if r₀ > 0, r₁ > 0 and τ > θ₀ - θ₁ ≥ π", function(test) {
203 var a = shape.arc().innerRadius(50).outerRadius(100);
204 test.pathEqual(a.startAngle(0).endAngle(-3 * Math.PI / 2)(), "M0,-100A100,100,0,1,0,100,0L50,0A50,50,0,1,1,0,-50Z");
205 test.pathEqual(a.startAngle(-2 * Math.PI).endAngle(-7 * Math.PI / 2)(), "M0,-100A100,100,0,1,0,100,0L50,0A50,50,0,1,1,0,-50Z");
206 test.pathEqual(a.startAngle(Math.PI).endAngle(-Math.PI / 2)(), "M0,100A100,100,0,1,0,-100,0L-50,0A50,50,0,1,1,0,50Z");
207 test.end();
208 });
209
210 tape("arc().innerRadius(0).outerRadius(0).cornerRadius(r) renders a point", function(test) {
211 var a = shape.arc().innerRadius(0).outerRadius(0).cornerRadius(5);
212 test.pathEqual(a.startAngle(0).endAngle(2 * Math.PI)(), "M0,0Z");
213 test.pathEqual(a.startAngle(0).endAngle(0)(), "M0,0Z");
214 test.end();
215 });
216
217 tape("arc().innerRadius(0).outerRadius(r).startAngle(θ₀).endAngle(θ₁).cornerRadius(rᵧ) renders a clockwise circle if r > 0 and θ₁ - θ₀ ≥ τ", function(test) {
218 var a = shape.arc().innerRadius(0).outerRadius(100).cornerRadius(5);
219 test.pathEqual(a.startAngle(0).endAngle(2 * Math.PI)(), "M0,-100A100,100,0,1,1,0,100A100,100,0,1,1,0,-100Z");
220 test.pathEqual(a.startAngle(0).endAngle(3 * Math.PI)(), "M0,-100A100,100,0,1,1,0,100A100,100,0,1,1,0,-100Z");
221 test.pathEqual(a.startAngle(-2 * Math.PI).endAngle(0)(), "M0,-100A100,100,0,1,1,0,100A100,100,0,1,1,0,-100Z");
222 test.pathEqual(a.startAngle(-Math.PI).endAngle(Math.PI)(), "M0,100A100,100,0,1,1,0,-100A100,100,0,1,1,0,100Z");
223 test.pathEqual(a.startAngle(-3 * Math.PI).endAngle(0)(), "M0,100A100,100,0,1,1,0,-100A100,100,0,1,1,0,100Z");
224 test.end();
225 });
226
227 tape("arc().innerRadius(0).outerRadius(r).startAngle(θ₀).endAngle(θ₁).cornerRadius(rᵧ) renders an anticlockwise circle if r > 0 and θ₀ - θ₁ ≥ τ", function(test) {
228 var a = shape.arc().innerRadius(0).outerRadius(100).cornerRadius(5);
229 test.pathEqual(a.startAngle(0).endAngle(-2 * Math.PI)(), "M0,-100A100,100,0,1,0,0,100A100,100,0,1,0,0,-100Z");
230 test.pathEqual(a.startAngle(0).endAngle(-3 * Math.PI)(), "M0,-100A100,100,0,1,0,0,100A100,100,0,1,0,0,-100Z");
231 test.pathEqual(a.startAngle(2 * Math.PI).endAngle(0)(), "M0,-100A100,100,0,1,0,0,100A100,100,0,1,0,0,-100Z");
232 test.pathEqual(a.startAngle(Math.PI).endAngle(-Math.PI)(), "M0,100A100,100,0,1,0,0,-100A100,100,0,1,0,0,100Z");
233 test.pathEqual(a.startAngle(3 * Math.PI).endAngle(0)(), "M0,100A100,100,0,1,0,0,-100A100,100,0,1,0,0,100Z");
234 test.end();
235 });
236
237 // Note: The outer ring starts and ends at θ₀, but the inner ring starts and ends at θ₁.
238 // Note: The outer ring is clockwise, but the inner ring is anticlockwise.
239 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(θ₀).endAngle(θ₁).cornerRadius(rᵧ) renders a clockwise annulus if r₀ > 0, r₁ > 0 and θ₀ - θ₁ ≥ τ", function(test) {
240 var a = shape.arc().innerRadius(50).outerRadius(100).cornerRadius(5);
241 test.pathEqual(a.startAngle(0).endAngle(2 * Math.PI)(), "M0,-100A100,100,0,1,1,0,100A100,100,0,1,1,0,-100M0,-50A50,50,0,1,0,0,50A50,50,0,1,0,0,-50Z");
242 test.pathEqual(a.startAngle(0).endAngle(3 * Math.PI)(), "M0,-100A100,100,0,1,1,0,100A100,100,0,1,1,0,-100M0,50A50,50,0,1,0,0,-50A50,50,0,1,0,0,50Z");
243 test.pathEqual(a.startAngle(-2 * Math.PI).endAngle(0)(), "M0,-100A100,100,0,1,1,0,100A100,100,0,1,1,0,-100M0,-50A50,50,0,1,0,0,50A50,50,0,1,0,0,-50Z");
244 test.pathEqual(a.startAngle(-Math.PI).endAngle(Math.PI)(), "M0,100A100,100,0,1,1,0,-100A100,100,0,1,1,0,100M0,50A50,50,0,1,0,0,-50A50,50,0,1,0,0,50Z");
245 test.pathEqual(a.startAngle(-3 * Math.PI).endAngle(0)(), "M0,100A100,100,0,1,1,0,-100A100,100,0,1,1,0,100M0,-50A50,50,0,1,0,0,50A50,50,0,1,0,0,-50Z");
246 test.end();
247 });
248
249 // Note: The outer ring starts and ends at θ₀, but the inner ring starts and ends at θ₁.
250 // Note: The outer ring is anticlockwise, but the inner ring is clockwise.
251 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(θ₀).endAngle(θ₁).cornerRadius(rᵧ) renders an anticlockwise annulus if r₀ > 0, r₁ > 0 and θ₁ - θ₀ ≥ τ", function(test) {
252 var a = shape.arc().innerRadius(50).outerRadius(100).cornerRadius(5);
253 test.pathEqual(a.startAngle(0).endAngle(-2 * Math.PI)(), "M0,-100A100,100,0,1,0,0,100A100,100,0,1,0,0,-100M0,-50A50,50,0,1,1,0,50A50,50,0,1,1,0,-50Z");
254 test.pathEqual(a.startAngle(0).endAngle(-3 * Math.PI)(), "M0,-100A100,100,0,1,0,0,100A100,100,0,1,0,0,-100M0,50A50,50,0,1,1,0,-50A50,50,0,1,1,0,50Z");
255 test.pathEqual(a.startAngle(2 * Math.PI).endAngle(0)(), "M0,-100A100,100,0,1,0,0,100A100,100,0,1,0,0,-100M0,-50A50,50,0,1,1,0,50A50,50,0,1,1,0,-50Z");
256 test.pathEqual(a.startAngle(Math.PI).endAngle(-Math.PI)(), "M0,100A100,100,0,1,0,0,-100A100,100,0,1,0,0,100M0,50A50,50,0,1,1,0,-50A50,50,0,1,1,0,50Z");
257 test.pathEqual(a.startAngle(3 * Math.PI).endAngle(0)(), "M0,100A100,100,0,1,0,0,-100A100,100,0,1,0,0,100M0,-50A50,50,0,1,1,0,50A50,50,0,1,1,0,-50Z");
258 test.end();
259 });
260
261 tape("arc().innerRadius(0).outerRadius(r).startAngle(θ₀).endAngle(θ₁).cornerRadius(rᵧ) renders a small clockwise sector if r > 0 and π > θ₁ - θ₀ ≥ 0", function(test) {
262 var a = shape.arc().innerRadius(0).outerRadius(100).cornerRadius(5);
263 test.pathEqual(a.startAngle(0).endAngle(Math.PI / 2)(), "M0,-94.868330A5,5,0,0,1,5.263158,-99.861400A100,100,0,0,1,99.861400,-5.263158A5,5,0,0,1,94.868330,0L0,0Z");
264 test.pathEqual(a.startAngle(2 * Math.PI).endAngle(5 * Math.PI / 2)(), "M0,-94.868330A5,5,0,0,1,5.263158,-99.861400A100,100,0,0,1,99.861400,-5.263158A5,5,0,0,1,94.868330,0L0,0Z");
265 test.pathEqual(a.startAngle(-Math.PI).endAngle(-Math.PI / 2)(), "M0,94.868330A5,5,0,0,1,-5.263158,99.861400A100,100,0,0,1,-99.861400,5.263158A5,5,0,0,1,-94.868330,0L0,0Z");
266 test.end();
267 });
268
269 tape("arc().innerRadius(0).outerRadius(r).startAngle(θ₀).endAngle(θ₁).cornerRadius(rᵧ) renders a small anticlockwise sector if r > 0 and π > θ₀ - θ₁ ≥ 0", function(test) {
270 var a = shape.arc().innerRadius(0).outerRadius(100).cornerRadius(5);
271 test.pathEqual(a.startAngle(0).endAngle(-Math.PI / 2)(), "M0,-94.868330A5,5,0,0,0,-5.263158,-99.861400A100,100,0,0,0,-99.861400,-5.263158A5,5,0,0,0,-94.868330,0L0,0Z");
272 test.pathEqual(a.startAngle(-2 * Math.PI).endAngle(-5 * Math.PI / 2)(), "M0,-94.868330A5,5,0,0,0,-5.263158,-99.861400A100,100,0,0,0,-99.861400,-5.263158A5,5,0,0,0,-94.868330,0L0,0Z");
273 test.pathEqual(a.startAngle(Math.PI).endAngle(Math.PI / 2)(), "M0,94.868330A5,5,0,0,0,5.263158,99.861400A100,100,0,0,0,99.861400,5.263158A5,5,0,0,0,94.868330,0L0,0Z");
274 test.end();
275 });
276
277 tape("arc().innerRadius(0).outerRadius(r).startAngle(θ₀).endAngle(θ₁).cornerRadius(rᵧ) renders a large clockwise sector if r > 0 and τ > θ₁ - θ₀ ≥ π", function(test) {
278 var a = shape.arc().innerRadius(0).outerRadius(100).cornerRadius(5);
279 test.pathEqual(a.startAngle(0).endAngle(3 * Math.PI / 2)(), "M0,-94.868330A5,5,0,0,1,5.263158,-99.861400A100,100,0,1,1,-99.861400,5.263158A5,5,0,0,1,-94.868330,0L0,0Z");
280 test.pathEqual(a.startAngle(2 * Math.PI).endAngle(7 * Math.PI / 2)(), "M0,-94.868330A5,5,0,0,1,5.263158,-99.861400A100,100,0,1,1,-99.861400,5.263158A5,5,0,0,1,-94.868330,0L0,0Z");
281 test.pathEqual(a.startAngle(-Math.PI).endAngle(Math.PI / 2)(), "M0,94.868330A5,5,0,0,1,-5.263158,99.861400A100,100,0,1,1,99.861400,-5.263158A5,5,0,0,1,94.868330,0L0,0Z");
282 test.end();
283 });
284
285 tape("arc().innerRadius(0).outerRadius(r).startAngle(θ₀).endAngle(θ₁).cornerRadius(rᵧ) renders a large anticlockwise sector if r > 0 and τ > θ₀ - θ₁ ≥ π", function(test) {
286 var a = shape.arc().innerRadius(0).outerRadius(100).cornerRadius(5);
287 test.pathEqual(a.startAngle(0).endAngle(-3 * Math.PI / 2)(), "M0,-94.868330A5,5,0,0,0,-5.263158,-99.861400A100,100,0,1,0,99.861400,5.263158A5,5,0,0,0,94.868330,0L0,0Z");
288 test.pathEqual(a.startAngle(-2 * Math.PI).endAngle(-7 * Math.PI / 2)(), "M0,-94.868330A5,5,0,0,0,-5.263158,-99.861400A100,100,0,1,0,99.861400,5.263158A5,5,0,0,0,94.868330,0L0,0Z");
289 test.pathEqual(a.startAngle(Math.PI).endAngle(-Math.PI / 2)(), "M0,94.868330A5,5,0,0,0,5.263158,99.861400A100,100,0,1,0,-99.861400,-5.263158A5,5,0,0,0,-94.868330,0L0,0Z");
290 test.end();
291 });
292
293 // Note: The outer ring is clockwise, but the inner ring is anticlockwise.
294 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(θ₀).endAngle(θ₁).cornerRadius(rᵧ) renders a small clockwise annular sector if r₀ > 0, r₁ > 0 and π > θ₁ - θ₀ ≥ 0", function(test) {
295 var a = shape.arc().innerRadius(50).outerRadius(100).cornerRadius(5);
296 test.pathEqual(a.startAngle(0).endAngle(Math.PI / 2)(), "M0,-94.868330A5,5,0,0,1,5.263158,-99.861400A100,100,0,0,1,99.861400,-5.263158A5,5,0,0,1,94.868330,0L54.772256,0A5,5,0,0,1,49.792960,-4.545455A50,50,0,0,0,4.545455,-49.792960A5,5,0,0,1,0,-54.772256Z");
297 test.pathEqual(a.startAngle(2 * Math.PI).endAngle(5 * Math.PI / 2)(), "M0,-94.868330A5,5,0,0,1,5.263158,-99.861400A100,100,0,0,1,99.861400,-5.263158A5,5,0,0,1,94.868330,0L54.772256,0A5,5,0,0,1,49.792960,-4.545455A50,50,0,0,0,4.545455,-49.792960A5,5,0,0,1,0,-54.772256Z");
298 test.pathEqual(a.startAngle(-Math.PI).endAngle(-Math.PI / 2)(), "M0,94.868330A5,5,0,0,1,-5.263158,99.861400A100,100,0,0,1,-99.861400,5.263158A5,5,0,0,1,-94.868330,0L-54.772256,0A5,5,0,0,1,-49.792960,4.545455A50,50,0,0,0,-4.545455,49.792960A5,5,0,0,1,0,54.772256Z");
299 test.end();
300 });
301
302 // Note: The outer ring is anticlockwise, but the inner ring is clockwise.
303 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(θ₀).endAngle(θ₁).cornerRadius(rᵧ) renders a small anticlockwise annular sector if r₀ > 0, r₁ > 0 and π > θ₀ - θ₁ ≥ 0", function(test) {
304 var a = shape.arc().innerRadius(50).outerRadius(100).cornerRadius(5);
305 test.pathEqual(a.startAngle(0).endAngle(-Math.PI / 2)(), "M0,-94.868330A5,5,0,0,0,-5.263158,-99.861400A100,100,0,0,0,-99.861400,-5.263158A5,5,0,0,0,-94.868330,0L-54.772256,0A5,5,0,0,0,-49.792960,-4.545455A50,50,0,0,1,-4.545455,-49.792960A5,5,0,0,0,0,-54.772256Z");
306 test.pathEqual(a.startAngle(-2 * Math.PI).endAngle(-5 * Math.PI / 2)(), "M0,-94.868330A5,5,0,0,0,-5.263158,-99.861400A100,100,0,0,0,-99.861400,-5.263158A5,5,0,0,0,-94.868330,0L-54.772256,0A5,5,0,0,0,-49.792960,-4.545455A50,50,0,0,1,-4.545455,-49.792960A5,5,0,0,0,0,-54.772256Z");
307 test.pathEqual(a.startAngle(Math.PI).endAngle(Math.PI / 2)(), "M0,94.868330A5,5,0,0,0,5.263158,99.861400A100,100,0,0,0,99.861400,5.263158A5,5,0,0,0,94.868330,0L54.772256,0A5,5,0,0,0,49.792960,4.545455A50,50,0,0,1,4.545455,49.792960A5,5,0,0,0,0,54.772256Z");
308 test.end();
309 });
310
311 // Note: The outer ring is clockwise, but the inner ring is anticlockwise.
312 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(θ₀).endAngle(θ₁).cornerRadius(rᵧ) renders a large clockwise annular sector if r₀ > 0, r₁ > 0 and τ > θ₁ - θ₀ ≥ π", function(test) {
313 var a = shape.arc().innerRadius(50).outerRadius(100).cornerRadius(5);
314 test.pathEqual(a.startAngle(0).endAngle(3 * Math.PI / 2)(), "M0,-94.868330A5,5,0,0,1,5.263158,-99.861400A100,100,0,1,1,-99.861400,5.263158A5,5,0,0,1,-94.868330,0L-54.772256,0A5,5,0,0,1,-49.792960,4.545455A50,50,0,1,0,4.545455,-49.792960A5,5,0,0,1,0,-54.772256Z");
315 test.pathEqual(a.startAngle(2 * Math.PI).endAngle(7 * Math.PI / 2)(), "M0,-94.868330A5,5,0,0,1,5.263158,-99.861400A100,100,0,1,1,-99.861400,5.263158A5,5,0,0,1,-94.868330,0L-54.772256,0A5,5,0,0,1,-49.792960,4.545455A50,50,0,1,0,4.545455,-49.792960A5,5,0,0,1,0,-54.772256Z");
316 test.pathEqual(a.startAngle(-Math.PI).endAngle(Math.PI / 2)(), "M0,94.868330A5,5,0,0,1,-5.263158,99.861400A100,100,0,1,1,99.861400,-5.263158A5,5,0,0,1,94.868330,0L54.772256,0A5,5,0,0,1,49.792960,-4.545455A50,50,0,1,0,-4.545455,49.792960A5,5,0,0,1,0,54.772256Z");
317 test.end();
318 });
319
320 // Note: The outer ring is anticlockwise, but the inner ring is clockwise.
321 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(θ₀).endAngle(θ₁).cornerRadius(rᵧ) renders a large anticlockwise annular sector if r₀ > 0, r₁ > 0 and τ > θ₀ - θ₁ ≥ π", function(test) {
322 var a = shape.arc().innerRadius(50).outerRadius(100).cornerRadius(5);
323 test.pathEqual(a.startAngle(0).endAngle(-3 * Math.PI / 2)(), "M0,-94.868330A5,5,0,0,0,-5.263158,-99.861400A100,100,0,1,0,99.861400,5.263158A5,5,0,0,0,94.868330,0L54.772256,0A5,5,0,0,0,49.792960,4.545455A50,50,0,1,1,-4.545455,-49.792960A5,5,0,0,0,0,-54.772256Z");
324 test.pathEqual(a.startAngle(-2 * Math.PI).endAngle(-7 * Math.PI / 2)(), "M0,-94.868330A5,5,0,0,0,-5.263158,-99.861400A100,100,0,1,0,99.861400,5.263158A5,5,0,0,0,94.868330,0L54.772256,0A5,5,0,0,0,49.792960,4.545455A50,50,0,1,1,-4.545455,-49.792960A5,5,0,0,0,0,-54.772256Z");
325 test.pathEqual(a.startAngle(Math.PI).endAngle(-Math.PI / 2)(), "M0,94.868330A5,5,0,0,0,5.263158,99.861400A100,100,0,1,0,-99.861400,-5.263158A5,5,0,0,0,-94.868330,0L-54.772256,0A5,5,0,0,0,-49.792960,-4.545455A50,50,0,1,1,4.545455,49.792960A5,5,0,0,0,0,54.772256Z");
326 test.end();
327 });
328
329 tape("arc().innerRadius(r₀).outerRadius(r₁).cornerRadius(rᵧ) restricts rᵧ to |r₁ - r₀| / 2", function(test) {
330 var a = shape.arc().cornerRadius(Infinity).startAngle(0).endAngle(Math.PI / 2);
331 test.pathEqual(a.innerRadius(90).outerRadius(100)(), "M0,-94.868330A5,5,0,0,1,5.263158,-99.861400A100,100,0,0,1,99.861400,-5.263158A5,5,0,0,1,94.868330,0L94.868330,0A5,5,0,0,1,89.875260,-4.736842A90,90,0,0,0,4.736842,-89.875260A5,5,0,0,1,0,-94.868330Z");
332 test.pathEqual(a.innerRadius(100).outerRadius(90)(), "M0,-94.868330A5,5,0,0,1,5.263158,-99.861400A100,100,0,0,1,99.861400,-5.263158A5,5,0,0,1,94.868330,0L94.868330,0A5,5,0,0,1,89.875260,-4.736842A90,90,0,0,0,4.736842,-89.875260A5,5,0,0,1,0,-94.868330Z");
333 test.end();
334 });
335
336 tape("arc().innerRadius(r₀).outerRadius(r₁).cornerRadius(rᵧ) merges adjacent corners when rᵧ is relatively large", function(test) {
337 var a = shape.arc().cornerRadius(Infinity).startAngle(0).endAngle(Math.PI / 2);
338 test.pathEqual(a.innerRadius(10).outerRadius(100)(), "M0,-41.421356A41.421356,41.421356,0,1,1,41.421356,0L24.142136,0A24.142136,24.142136,0,0,1,0,-24.142136Z");
339 test.pathEqual(a.innerRadius(100).outerRadius(10)(), "M0,-41.421356A41.421356,41.421356,0,1,1,41.421356,0L24.142136,0A24.142136,24.142136,0,0,1,0,-24.142136Z");
340 test.end();
341 });
342
343 tape("arc().innerRadius(0).outerRadius(0).startAngle(0).endAngle(τ).padAngle(δ) does not pad a point", function(test) {
344 var a = shape.arc().innerRadius(0).outerRadius(0).startAngle(0).endAngle(2 * Math.PI).padAngle(0.1);
345 test.pathEqual(a(), "M0,0Z");
346 test.end();
347 });
348
349 tape("arc().innerRadius(0).outerRadius(r).startAngle(0).endAngle(τ).padAngle(δ) does not pad a circle", function(test) {
350 var a = shape.arc().innerRadius(0).outerRadius(100).startAngle(0).endAngle(2 * Math.PI).padAngle(0.1);
351 test.pathEqual(a(), "M0,-100A100,100,0,1,1,0,100A100,100,0,1,1,0,-100Z");
352 test.end();
353 });
354
355 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(0).endAngle(τ).padAngle(δ) does not pad an annulus", function(test) {
356 var a = shape.arc().innerRadius(50).outerRadius(100).startAngle(0).endAngle(2 * Math.PI).padAngle(0.1);
357 test.pathEqual(a(), "M0,-100A100,100,0,1,1,0,100A100,100,0,1,1,0,-100M0,-50A50,50,0,1,0,0,50A50,50,0,1,0,0,-50Z");
358 test.end();
359 });
360
361 tape("arc().innerRadius(0).outerRadius(r).startAngle(θ₀).endAngle(θ₁).padAngle(δ) pads the outside of a circular sector", function(test) {
362 var a = shape.arc().innerRadius(0).outerRadius(100).startAngle(0).endAngle(Math.PI / 2).padAngle(0.1);
363 test.pathEqual(a(), "M4.997917,-99.875026A100,100,0,0,1,99.875026,-4.997917L0,0Z");
364 test.end();
365 });
366
367 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(θ₀).endAngle(θ₁).padAngle(δ) pads an annular sector", function(test) {
368 var a = shape.arc().innerRadius(50).outerRadius(100).startAngle(0).endAngle(Math.PI / 2).padAngle(0.1);
369 test.pathEqual(a(), "M5.587841,-99.843758A100,100,0,0,1,99.843758,-5.587841L49.686779,-5.587841A50,50,0,0,0,5.587841,-49.686779Z");
370 test.end();
371 });
372
373 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(θ₀).endAngle(θ₁).padAngle(δ) may collapse the inside of an annular sector", function(test) {
374 var a = shape.arc().innerRadius(10).outerRadius(100).startAngle(0).endAngle(Math.PI / 2).padAngle(0.2);
375 test.pathEqual(a(), "M10.033134,-99.495408A100,100,0,0,1,99.495408,-10.033134L7.071068,-7.071068Z");
376 test.end();
377 });
378
379 tape("arc().innerRadius(0).outerRadius(r).startAngle(θ₀).endAngle(θ₁).padAngle(δ).cornerRadius(rᵧ) rounds and pads a circular sector", function(test) {
380 var a = shape.arc().innerRadius(0).outerRadius(100).startAngle(0).endAngle(Math.PI / 2).padAngle(0.1).cornerRadius(10);
381 test.pathEqual(a(), "M4.470273,-89.330939A10,10,0,0,1,16.064195,-98.701275A100,100,0,0,1,98.701275,-16.064195A10,10,0,0,1,89.330939,-4.470273L0,0Z");
382 test.end();
383 });
384
385 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(θ₀).endAngle(θ₁).padAngle(δ).cornerRadius(rᵧ) rounds and pads an annular sector", function(test) {
386 var a = shape.arc().innerRadius(50).outerRadius(100).startAngle(0).endAngle(Math.PI / 2).padAngle(0.1).cornerRadius(10);
387 test.pathEqual(a(), "M5.587841,-88.639829A10,10,0,0,1,17.319823,-98.488698A100,100,0,0,1,98.488698,-17.319823A10,10,0,0,1,88.639829,-5.587841L57.939790,-5.587841A10,10,0,0,1,48.283158,-12.989867A50,50,0,0,0,12.989867,-48.283158A10,10,0,0,1,5.587841,-57.939790Z");
388 test.end();
389 });
390
391 tape("arc().innerRadius(r₀).outerRadius(r₁).startAngle(θ₀).endAngle(θ₁).padAngle(δ).cornerRadius(rᵧ) rounds and pads a collapsed annular sector", function(test) {
392 var a = shape.arc().innerRadius(10).outerRadius(100).startAngle(0).endAngle(Math.PI / 2).padAngle(0.2).cornerRadius(10);
393 test.pathEqual(a(), "M9.669396,-88.145811A10,10,0,0,1,21.849183,-97.583878A100,100,0,0,1,97.583878,-21.849183A10,10,0,0,1,88.145811,-9.669396L7.071068,-7.071068Z");
394 test.end();
395 });
0 var tape = require("tape"),
1 shape = require("../");
2
3 require("./pathEqual");
4
5 tape("area() returns a default area shape", function(test) {
6 var a = shape.area();
7 test.equal(a.x0()([42, 34]), 42);
8 test.equal(a.x1(), null);
9 test.equal(a.y0()([42, 34]), 0);
10 test.equal(a.y1()([42, 34]), 34);
11 test.equal(a.defined()([42, 34]), true);
12 test.equal(a.curve(), shape.curveLinear);
13 test.equal(a.context(), null);
14 test.pathEqual(a([[0, 1], [2, 3], [4, 5]]), "M0,1L2,3L4,5L4,0L2,0L0,0Z");
15 test.end();
16 });
17
18 tape("area.x(f)(data) passes d, i and data to the specified function f", function(test) {
19 var data = ["a", "b"], actual = [];
20 shape.area().x(function() { actual.push([].slice.call(arguments)); })(data);
21 test.deepEqual(actual, [["a", 0, data], ["b", 1, data]]);
22 test.end();
23 });
24
25 tape("area.x0(f)(data) passes d, i and data to the specified function f", function(test) {
26 var data = ["a", "b"], actual = [];
27 shape.area().x0(function() { actual.push([].slice.call(arguments)); })(data);
28 test.deepEqual(actual, [["a", 0, data], ["b", 1, data]]);
29 test.end();
30 });
31
32 tape("area.x1(f)(data) passes d, i and data to the specified function f", function(test) {
33 var data = ["a", "b"], actual = [];
34 shape.area().x1(function() { actual.push([].slice.call(arguments)); })(data);
35 test.deepEqual(actual, [["a", 0, data], ["b", 1, data]]);
36 test.end();
37 });
38
39 tape("area.y(f)(data) passes d, i and data to the specified function f", function(test) {
40 var data = ["a", "b"], actual = [];
41 shape.area().y(function() { actual.push([].slice.call(arguments)); })(data);
42 test.deepEqual(actual, [["a", 0, data], ["b", 1, data]]);
43 test.end();
44 });
45
46 tape("area.y0(f)(data) passes d, i and data to the specified function f", function(test) {
47 var data = ["a", "b"], actual = [];
48 shape.area().y0(function() { actual.push([].slice.call(arguments)); })(data);
49 test.deepEqual(actual, [["a", 0, data], ["b", 1, data]]);
50 test.end();
51 });
52
53 tape("area.y1(f)(data) passes d, i and data to the specified function f", function(test) {
54 var data = ["a", "b"], actual = [];
55 shape.area().y1(function() { actual.push([].slice.call(arguments)); })(data);
56 test.deepEqual(actual, [["a", 0, data], ["b", 1, data]]);
57 test.end();
58 });
59
60 tape("area.defined(f)(data) passes d, i and data to the specified function f", function(test) {
61 var data = ["a", "b"], actual = [];
62 shape.area().defined(function() { actual.push([].slice.call(arguments)); })(data);
63 test.deepEqual(actual, [["a", 0, data], ["b", 1, data]]);
64 test.end();
65 });
66
67 tape("area.x(x)(data) observes the specified function", function(test) {
68 var x = function(d) { return d.x; },
69 a = shape.area().x(x);
70 test.equal(a.x(), x);
71 test.equal(a.x0(), x);
72 test.equal(a.x1(), null);
73 test.pathEqual(a([{x: 0, 1: 1}, {x: 2, 1: 3}, {x: 4, 1: 5}]), "M0,1L2,3L4,5L4,0L2,0L0,0Z");
74 test.end();
75 });
76
77 tape("area.x(x)(data) observes the specified constant", function(test) {
78 var x = 0,
79 a = shape.area().x(x);
80 test.equal(a.x()(), 0);
81 test.equal(a.x0()(), 0);
82 test.equal(a.x1(), null);
83 test.pathEqual(a([{1: 1}, {1: 3}, {1: 5}]), "M0,1L0,3L0,5L0,0L0,0L0,0Z");
84 test.end();
85 });
86
87 tape("area.y(y)(data) observes the specified function", function(test) {
88 var y = function(d) { return d.y; },
89 a = shape.area().y(y);
90 test.equal(a.y(), y);
91 test.equal(a.y0(), y);
92 test.equal(a.y1(), null);
93 test.pathEqual(a([{0: 0, y: 1}, {0: 2, y: 3}, {0: 4, y: 5}]), "M0,1L2,3L4,5L4,5L2,3L0,1Z");
94 test.end();
95 });
96
97 tape("area.y(y)(data) observes the specified constant", function(test) {
98 var a = shape.area().y(0);
99 test.equal(a.y()(), 0);
100 test.equal(a.y0()(), 0);
101 test.equal(a.y1(), null);
102 test.pathEqual(a([{0: 0}, {0: 2}, {0: 4}]), "M0,0L2,0L4,0L4,0L2,0L0,0Z");
103 test.end();
104 });
105
106 tape("area.curve(curve) sets the curve method", function(test) {
107 var a = shape.area().curve(shape.curveCardinal);
108 test.pathEqual(a([[0, 1], [1, 3], [2, 1], [3, 3]]), "M0,1C0,1,0.666667,3,1,3C1.333333,3,1.666667,1,2,1C2.333333,1,3,3,3,3L3,0C3,0,2.333333,0,2,0C1.666667,0,1.333333,0,1,0C0.666667,0,0,0,0,0Z");
109 test.end();
110 });
111
112 tape("area.curve(curveCardinal.tension(tension)) sets the cardinal spline tension", function(test) {
113 var a = shape.area().curve(shape.curveCardinal.tension(0.1));
114 test.equal(a([]), null);
115 test.pathEqual(a([[0, 1]]), "M0,1L0,0Z");
116 test.pathEqual(a([[0, 1], [1, 3]]), "M0,1L1,3L1,0L0,0Z");
117 test.pathEqual(a([[0, 1], [1, 3], [2, 1]]), "M0,1C0,1,0.700000,3,1,3C1.300000,3,2,1,2,1L2,0C2,0,1.300000,0,1,0C0.700000,0,0,0,0,0Z");
118 test.pathEqual(a([[0, 1], [1, 3], [2, 1], [3, 3]]), "M0,1C0,1,0.700000,3,1,3C1.300000,3,1.700000,1,2,1C2.300000,1,3,3,3,3L3,0C3,0,2.300000,0,2,0C1.700000,0,1.300000,0,1,0C0.700000,0,0,0,0,0Z");
119 test.end();
120 });
121
122 tape("area.curve(curveCardinal.tension(tension)) coerces the specified tension to a number", function(test) {
123 var a = shape.area().curve(shape.curveCardinal.tension("0.1"));
124 test.equal(a([]), null);
125 test.pathEqual(a([[0, 1]]), "M0,1L0,0Z");
126 test.pathEqual(a([[0, 1], [1, 3]]), "M0,1L1,3L1,0L0,0Z");
127 test.pathEqual(a([[0, 1], [1, 3], [2, 1]]), "M0,1C0,1,0.700000,3,1,3C1.300000,3,2,1,2,1L2,0C2,0,1.300000,0,1,0C0.700000,0,0,0,0,0Z");
128 test.pathEqual(a([[0, 1], [1, 3], [2, 1], [3, 3]]), "M0,1C0,1,0.700000,3,1,3C1.300000,3,1.700000,1,2,1C2.300000,1,3,3,3,3L3,0C3,0,2.300000,0,2,0C1.700000,0,1.300000,0,1,0C0.700000,0,0,0,0,0Z");
129 test.end();
130 });
131
132 tape("area.lineX0() returns a line derived from the area", function(test) {
133 var defined = function() { return true; },
134 curve = shape.curveCardinal,
135 context = {},
136 x0 = function() {},
137 x1 = function() {},
138 y = function() {},
139 a = shape.area().defined(defined).curve(curve).context(context).y(y).x0(x0).x1(x1),
140 l = a.lineX0();
141 test.equal(l.defined(), defined);
142 test.equal(l.curve(), curve);
143 test.equal(l.context(), context);
144 test.equal(l.x(), x0);
145 test.equal(l.y(), y);
146 test.end();
147 });
148
149 tape("area.lineX1() returns a line derived from the area", function(test) {
150 var defined = function() { return true; },
151 curve = shape.curveCardinal,
152 context = {},
153 x0 = function() {},
154 x1 = function() {},
155 y = function() {},
156 a = shape.area().defined(defined).curve(curve).context(context).y(y).x0(x0).x1(x1),
157 l = a.lineX1();
158 test.equal(l.defined(), defined);
159 test.equal(l.curve(), curve);
160 test.equal(l.context(), context);
161 test.equal(l.x(), x1);
162 test.equal(l.y(), y);
163 test.end();
164 });
165
166 tape("area.lineY0() returns a line derived from the area", function(test) {
167 var defined = function() { return true; },
168 curve = shape.curveCardinal,
169 context = {},
170 x = function() {},
171 y0 = function() {},
172 y1 = function() {},
173 a = shape.area().defined(defined).curve(curve).context(context).x(x).y0(y0).y1(y1),
174 l = a.lineY0();
175 test.equal(l.defined(), defined);
176 test.equal(l.curve(), curve);
177 test.equal(l.context(), context);
178 test.equal(l.x(), x);
179 test.equal(l.y(), y0);
180 test.end();
181 });
182
183 tape("area.lineY1() returns a line derived from the area", function(test) {
184 var defined = function() { return true; },
185 curve = shape.curveCardinal,
186 context = {},
187 x = function() {},
188 y0 = function() {},
189 y1 = function() {},
190 a = shape.area().defined(defined).curve(curve).context(context).x(x).y0(y0).y1(y1),
191 l = a.lineY1();
192 test.equal(l.defined(), defined);
193 test.equal(l.curve(), curve);
194 test.equal(l.context(), context);
195 test.equal(l.x(), x);
196 test.equal(l.y(), y1);
197 test.end();
198 });
0 var tape = require("tape"),
1 shape = require("../");
2
3 require("./pathEqual");
4
5 tape("areaRadial() returns a default radial area shape", function(test) {
6 var a = shape.areaRadial();
7 test.equal(a.startAngle()([42, 34]), 42);
8 test.equal(a.endAngle(), null);
9 test.equal(a.innerRadius()([42, 34]), 0);
10 test.equal(a.outerRadius()([42, 34]), 34);
11 test.equal(a.defined()([42, 34]), true);
12 test.equal(a.curve(), shape.curveLinear);
13 test.equal(a.context(), null);
14 test.pathEqual(a([[0, 1], [2, 3], [4, 5]]), "M0,-1L2.727892,1.248441L-3.784012,3.268218L0,0L0,0L0,0Z");
15 test.end();
16 });
17
18 tape("areaRadial.lineStartAngle() returns a line derived from the area", function(test) {
19 var defined = function() { return true; },
20 curve = shape.curveCardinal,
21 context = {},
22 startAngle = function() {},
23 endAngle = function() {},
24 radius = function() {},
25 a = shape.areaRadial().defined(defined).curve(curve).context(context).radius(radius).startAngle(startAngle).endAngle(endAngle),
26 l = a.lineStartAngle();
27 test.equal(l.defined(), defined);
28 test.equal(l.curve(), curve);
29 test.equal(l.context(), context);
30 test.equal(l.angle(), startAngle);
31 test.equal(l.radius(), radius);
32 test.end();
33 });
34
35 tape("areaRadial.lineEndAngle() returns a line derived from the area", function(test) {
36 var defined = function() { return true; },
37 curve = shape.curveCardinal,
38 context = {},
39 startAngle = function() {},
40 endAngle = function() {},
41 radius = function() {},
42 a = shape.areaRadial().defined(defined).curve(curve).context(context).radius(radius).startAngle(startAngle).endAngle(endAngle),
43 l = a.lineEndAngle();
44 test.equal(l.defined(), defined);
45 test.equal(l.curve(), curve);
46 test.equal(l.context(), context);
47 test.equal(l.angle(), endAngle);
48 test.equal(l.radius(), radius);
49 test.end();
50 });
51
52 tape("areaRadial.lineInnerRadius() returns a line derived from the area", function(test) {
53 var defined = function() { return true; },
54 curve = shape.curveCardinal,
55 context = {},
56 angle = function() {},
57 innerRadius = function() {},
58 outerRadius = function() {},
59 a = shape.areaRadial().defined(defined).curve(curve).context(context).angle(angle).innerRadius(innerRadius).outerRadius(outerRadius),
60 l = a.lineInnerRadius();
61 test.equal(l.defined(), defined);
62 test.equal(l.curve(), curve);
63 test.equal(l.context(), context);
64 test.equal(l.angle(), angle);
65 test.equal(l.radius(), innerRadius);
66 test.end();
67 });
68
69 tape("areaRadial.lineOuterRadius() returns a line derived from the area", function(test) {
70 var defined = function() { return true; },
71 curve = shape.curveCardinal,
72 context = {},
73 angle = function() {},
74 innerRadius = function() {},
75 outerRadius = function() {},
76 a = shape.areaRadial().defined(defined).curve(curve).context(context).angle(angle).innerRadius(innerRadius).outerRadius(outerRadius),
77 l = a.lineOuterRadius();
78 test.equal(l.defined(), defined);
79 test.equal(l.curve(), curve);
80 test.equal(l.context(), context);
81 test.equal(l.angle(), angle);
82 test.equal(l.radius(), outerRadius);
83 test.end();
84 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveBasis)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveBasis);
7 test.equal(l([]), null);
8 test.pathEqual(l([[0, 1]]), "M0,1Z");
9 test.pathEqual(l([[0, 1], [1, 3]]), "M0,1L1,3");
10 test.pathEqual(l([[0, 1], [1, 3], [2, 1]]), "M0,1L0.166667,1.333333C0.333333,1.666667,0.666667,2.333333,1,2.333333C1.333333,2.333333,1.666667,1.666667,1.833333,1.333333L2,1");
11 test.end();
12 });
13
14 tape("area.curve(curveBasis)(data) generates the expected path", function(test) {
15 var a = shape.area().curve(shape.curveBasis);
16 test.equal(a([]), null);
17 test.pathEqual(a([[0, 1]]), "M0,1L0,0Z");
18 test.pathEqual(a([[0, 1], [1, 3]]), "M0,1L1,3L1,0L0,0Z");
19 test.pathEqual(a([[0, 1], [1, 3], [2, 1]]), "M0,1L0.166667,1.333333C0.333333,1.666667,0.666667,2.333333,1,2.333333C1.333333,2.333333,1.666667,1.666667,1.833333,1.333333L2,1L2,0L1.833333,0C1.666667,0,1.333333,0,1,0C0.666667,0,0.333333,0,0.166667,0L0,0Z");
20 test.end();
21 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveBasisClosed)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveBasisClosed);
7 test.equal(l([]), null);
8 test.pathEqual(l([[0, 0]]), "M0,0Z");
9 test.pathEqual(l([[0, 0], [0, 10]]), "M0,6.666667L0,3.333333Z");
10 test.pathEqual(l([[0, 0], [0, 10], [10, 10]]), "M1.666667,8.333333C3.333333,10,6.666667,10,6.666667,8.333333C6.666667,6.666667,3.333333,3.333333,1.666667,3.333333C0,3.333333,0,6.666667,1.666667,8.333333");
11 test.pathEqual(l([[0, 0], [0, 10], [10, 10], [10, 0]]), "M1.666667,8.333333C3.333333,10,6.666667,10,8.333333,8.333333C10,6.666667,10,3.333333,8.333333,1.666667C6.666667,0,3.333333,0,1.666667,1.666667C0,3.333333,0,6.666667,1.666667,8.333333");
12 test.pathEqual(l([[0, 0], [0, 10], [10, 10], [10, 0], [0, 0]]), "M1.666667,8.333333C3.333333,10,6.666667,10,8.333333,8.333333C10,6.666667,10,3.333333,8.333333,1.666667C6.666667,0,3.333333,0,1.666667,0C0,0,0,0,0,1.666667C0,3.333333,0,6.666667,1.666667,8.333333");
13 test.end();
14 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveBasisOpen)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveBasisOpen);
7 test.equal(l([]), null);
8 test.equal(l([[0, 0]]), null);
9 test.equal(l([[0, 0], [0, 10]]), null);
10 test.pathEqual(l([[0, 0], [0, 10], [10, 10]]), "M1.666667,8.333333Z");
11 test.pathEqual(l([[0, 0], [0, 10], [10, 10], [10, 0]]), "M1.666667,8.333333C3.333333,10,6.666667,10,8.333333,8.333333");
12 test.pathEqual(l([[0, 0], [0, 10], [10, 10], [10, 0], [0, 0]]), "M1.666667,8.333333C3.333333,10,6.666667,10,8.333333,8.333333C10,6.666667,10,3.333333,8.333333,1.666667");
13 test.end();
14 });
15
16 tape("area.curve(curveBasisOpen)(data) generates the expected path", function(test) {
17 var a = shape.area().curve(shape.curveBasisOpen);
18 test.equal(a([]), null);
19 test.equal(a([[0, 1]]), null);
20 test.equal(a([[0, 1], [1, 3]]), null);
21 test.pathEqual(a([[0, 0], [0, 10], [10, 10]]), "M1.666667,8.333333L1.666667,0Z");
22 test.pathEqual(a([[0, 0], [0, 10], [10, 10], [10, 0]]), "M1.666667,8.333333C3.333333,10,6.666667,10,8.333333,8.333333L8.333333,0C6.666667,0,3.333333,0,1.666667,0Z");
23 test.pathEqual(a([[0, 0], [0, 10], [10, 10], [10, 0], [0, 0]]), "M1.666667,8.333333C3.333333,10,6.666667,10,8.333333,8.333333C10,6.666667,10,3.333333,8.333333,1.666667L8.333333,0C10,0,10,0,8.333333,0C6.666667,0,3.333333,0,1.666667,0Z");
24 test.end();
25 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveBundle) uses a default beta of 0.85", function(test) {
6 var l = shape.line().curve(shape.curveBundle.beta(0.85));
7 test.equal(shape.line().curve(shape.curveBundle)([[0, 1], [1, 3], [2, 1], [3, 3]]), l([[0, 1], [1, 3], [2, 1], [3, 3]]));
8 test.end();
9 });
10
11 tape("line.curve(curveBundle.beta(beta)) uses the specified beta", function(test) {
12 test.equal(shape.line().curve(shape.curveBundle.beta(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), "M0,1L0.16666666666666666,1.222222222222222C0.3333333333333333,1.4444444444444444,0.6666666666666666,1.8888888888888886,1,1.9999999999999998C1.3333333333333333,2.1111111111111107,1.6666666666666667,1.8888888888888886,2,2C2.3333333333333335,2.111111111111111,2.6666666666666665,2.5555555555555554,2.8333333333333335,2.7777777777777772L3,3");
13 test.end();
14 });
15
16 tape("line.curve(curveBundle.beta(beta)) coerces the specified beta to a number", function(test) {
17 var l = shape.line().curve(shape.curveBundle.beta("0.5"));
18 test.equal(shape.line().curve(shape.curveBundle.beta(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), l([[0, 1], [1, 3], [2, 1], [3, 3]]));
19 test.end();
20 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveCardinal)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveCardinal);
7 test.equal(l([]), null);
8 test.pathEqual(l([[0, 1]]), "M0,1Z");
9 test.pathEqual(l([[0, 1], [1, 3]]), "M0,1L1,3");
10 test.pathEqual(l([[0, 1], [1, 3], [2, 1]]), "M0,1C0,1,0.666667,3,1,3C1.333333,3,2,1,2,1");
11 test.pathEqual(l([[0, 1], [1, 3], [2, 1], [3, 3]]), "M0,1C0,1,0.666667,3,1,3C1.333333,3,1.666667,1,2,1C2.333333,1,3,3,3,3");
12 test.end();
13 });
14
15 tape("line.curve(curveCardinal) uses a default tension of zero", function(test) {
16 var l = shape.line().curve(shape.curveCardinal.tension(0));
17 test.equal(shape.line().curve(shape.curveCardinal)([[0, 1], [1, 3], [2, 1], [3, 3]]), l([[0, 1], [1, 3], [2, 1], [3, 3]]));
18 test.end();
19 });
20
21 tape("line.curve(curveCardinal.tension(tension)) uses the specified tension", function(test) {
22 test.pathEqual(shape.line().curve(shape.curveCardinal.tension(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), "M0,1C0,1,0.833333,3,1,3C1.166667,3,1.833333,1,2,1C2.166667,1,3,3,3,3");
23 test.end();
24 });
25
26 tape("line.curve(curveCardinal.tension(tension)) coerces the specified tension to a number", function(test) {
27 var l = shape.line().curve(shape.curveCardinal.tension("0.5"));
28 test.equal(shape.line().curve(shape.curveCardinal.tension(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), l([[0, 1], [1, 3], [2, 1], [3, 3]]));
29 test.end();
30 });
31
32 tape("area.curve(curveCardinal)(data) generates the expected path", function(test) {
33 var a = shape.area().curve(shape.curveCardinal);
34 test.equal(a([]), null);
35 test.pathEqual(a([[0, 1]]), "M0,1L0,0Z");
36 test.pathEqual(a([[0, 1], [1, 3]]), "M0,1L1,3L1,0L0,0Z");
37 test.pathEqual(a([[0, 1], [1, 3], [2, 1]]), "M0,1C0,1,0.666667,3,1,3C1.333333,3,2,1,2,1L2,0C2,0,1.333333,0,1,0C0.666667,0,0,0,0,0Z");
38 test.pathEqual(a([[0, 1], [1, 3], [2, 1], [3, 3]]), "M0,1C0,1,0.666667,3,1,3C1.333333,3,1.666667,1,2,1C2.333333,1,3,3,3,3L3,0C3,0,2.333333,0,2,0C1.666667,0,1.333333,0,1,0C0.666667,0,0,0,0,0Z");
39 test.end();
40 });
41
42 tape("area.curve(curveCardinal) uses a default tension of zero", function(test) {
43 var a = shape.area().curve(shape.curveCardinal.tension(0));
44 test.equal(shape.area().curve(shape.curveCardinal)([[0, 1], [1, 3], [2, 1], [3, 3]]), a([[0, 1], [1, 3], [2, 1], [3, 3]]));
45 test.end();
46 });
47
48 tape("area.curve(curveCardinal.tension(tension)) uses the specified tension", function(test) {
49 test.pathEqual(shape.area().curve(shape.curveCardinal.tension(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), "M0,1C0,1,0.833333,3,1,3C1.166667,3,1.833333,1,2,1C2.166667,1,3,3,3,3L3,0C3,0,2.166667,0,2,0C1.833333,0,1.166667,0,1,0C0.833333,0,0,0,0,0Z");
50 test.end();
51 });
52
53 tape("area.curve(curveCardinal.tension(tension)) coerces the specified tension to a number", function(test) {
54 var a = shape.area().curve(shape.curveCardinal.tension("0.5"));
55 test.equal(shape.area().curve(shape.curveCardinal.tension(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), a([[0, 1], [1, 3], [2, 1], [3, 3]]));
56 test.end();
57 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveCardinalClosed)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveCardinalClosed);
7 test.equal(l([]), null);
8 test.pathEqual(l([[0, 1]]), "M0,1Z");
9 test.pathEqual(l([[0, 1], [1, 3]]), "M1,3L0,1Z");
10 test.pathEqual(l([[0, 1], [1, 3], [2, 1]]), "M1,3C1.333333,3,2.166667,1.333333,2,1C1.833333,0.666667,0.166667,0.666667,0,1C-0.166667,1.333333,0.666667,3,1,3");
11 test.pathEqual(l([[0, 1], [1, 3], [2, 1], [3, 3]]), "M1,3C1.333333,3,1.666667,1,2,1C2.333333,1,3.333333,3,3,3C2.666667,3,0.333333,1,0,1C-0.333333,1,0.666667,3,1,3");
12 test.end();
13 });
14
15 tape("line.curve(curveCardinalClosed) uses a default tension of zero", function(test) {
16 var l = shape.line().curve(shape.curveCardinalClosed.tension(0));
17 test.equal(shape.line().curve(shape.curveCardinalClosed)([[0, 1], [1, 3], [2, 1], [3, 3]]), l([[0, 1], [1, 3], [2, 1], [3, 3]]));
18 test.end();
19 });
20
21 tape("line.curve(curveCardinalClosed.tension(tension)) uses the specified tension", function(test) {
22 test.pathEqual(shape.line().curve(shape.curveCardinalClosed.tension(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), "M1,3C1.166667,3,1.833333,1,2,1C2.166667,1,3.166667,3,3,3C2.833333,3,0.166667,1,0,1C-0.166667,1,0.833333,3,1,3");
23 test.end();
24 });
25
26 tape("line.curve(curveCardinalClosed.tension(tension)) coerces the specified tension to a number", function(test) {
27 var l = shape.line().curve(shape.curveCardinalClosed.tension("0.5"));
28 test.equal(shape.line().curve(shape.curveCardinalClosed.tension(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), l([[0, 1], [1, 3], [2, 1], [3, 3]]));
29 test.end();
30 });
31
32 tape("area.curve(curveCardinalClosed)(data) generates the expected path", function(test) {
33 var a = shape.area().curve(shape.curveCardinalClosed);
34 test.equal(a([]), null);
35 test.equal(a([[0, 1]]), "M0,1ZM0,0Z");
36 test.equal(a([[0, 1], [1, 3]]), "M1,3L0,1ZM0,0L1,0Z");
37 test.pathEqual(a([[0, 1], [1, 3], [2, 1]]), "M1,3C1.333333,3,2.166667,1.333333,2,1C1.833333,0.666667,0.166667,0.666667,0,1C-0.166667,1.333333,0.666667,3,1,3M1,0C0.666667,0,-0.166667,0,0,0C0.166667,0,1.833333,0,2,0C2.166667,0,1.333333,0,1,0");
38 test.pathEqual(a([[0, 1], [1, 3], [2, 1], [3, 3]]), "M1,3C1.333333,3,1.666667,1,2,1C2.333333,1,3.333333,3,3,3C2.666667,3,0.333333,1,0,1C-0.333333,1,0.666667,3,1,3M2,0C1.666667,0,1.333333,0,1,0C0.666667,0,-0.333333,0,0,0C0.333333,0,2.666667,0,3,0C3.333333,0,2.333333,0,2,0");
39 test.end();
40 });
41
42 tape("area.curve(curveCardinalClosed) uses a default tension of zero", function(test) {
43 var a = shape.area().curve(shape.curveCardinalClosed.tension(0));
44 test.equal(shape.area().curve(shape.curveCardinalClosed)([[0, 1], [1, 3], [2, 1], [3, 3]]), a([[0, 1], [1, 3], [2, 1], [3, 3]]));
45 test.end();
46 });
47
48 tape("area.curve(curveCardinalClosed.tension(tension)) uses the specified tension", function(test) {
49 test.pathEqual(shape.area().curve(shape.curveCardinalClosed.tension(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), "M1,3C1.166667,3,1.833333,1,2,1C2.166667,1,3.166667,3,3,3C2.833333,3,0.166667,1,0,1C-0.166667,1,0.833333,3,1,3M2,0C1.833333,0,1.166667,0,1,0C0.833333,0,-0.166667,0,0,0C0.166667,0,2.833333,0,3,0C3.166667,0,2.166667,0,2,0");
50 test.end();
51 });
52
53 tape("area.curve(curveCardinalClosed.tension(tension)) coerces the specified tension to a number", function(test) {
54 var a = shape.area().curve(shape.curveCardinalClosed.tension("0.5"));
55 test.equal(shape.area().curve(shape.curveCardinalClosed.tension(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), a([[0, 1], [1, 3], [2, 1], [3, 3]]));
56 test.end();
57 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveCardinalOpen)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveCardinalOpen);
7 test.equal(l([]), null);
8 test.equal(l([[0, 1]]), null);
9 test.equal(l([[0, 1], [1, 3]]), null);
10 test.pathEqual(l([[0, 1], [1, 3], [2, 1]]), "M1,3Z");
11 test.pathEqual(l([[0, 1], [1, 3], [2, 1], [3, 3]]), "M1,3C1.333333,3,1.666667,1,2,1");
12 test.end();
13 });
14
15 tape("line.curve(curveCardinalOpen) uses a default tension of zero", function(test) {
16 var l = shape.line().curve(shape.curveCardinalOpen.tension(0));
17 test.equal(shape.line().curve(shape.curveCardinalOpen)([[0, 1], [1, 3], [2, 1], [3, 3]]), l([[0, 1], [1, 3], [2, 1], [3, 3]]));
18 test.end();
19 });
20
21 tape("line.curve(curveCardinalOpen.tension(tension)) uses the specified tension", function(test) {
22 test.pathEqual(shape.line().curve(shape.curveCardinalOpen.tension(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), "M1,3C1.166667,3,1.833333,1,2,1");
23 test.end();
24 });
25
26 tape("line.curve(curveCardinalOpen.tension(tension)) coerces the specified tension to a number", function(test) {
27 var l = shape.line().curve(shape.curveCardinalOpen.tension("0.5"));
28 test.equal(shape.line().curve(shape.curveCardinalOpen.tension(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), l([[0, 1], [1, 3], [2, 1], [3, 3]]));
29 test.end();
30 });
31
32 tape("area.curve(curveCardinalOpen)(data) generates the expected path", function(test) {
33 var a = shape.area().curve(shape.curveCardinalOpen);
34 test.equal(a([]), null);
35 test.equal(a([[0, 1]]), null);
36 test.equal(a([[0, 1], [1, 3]]), null);
37 test.pathEqual(a([[0, 1], [1, 3], [2, 1]]), "M1,3L1,0Z");
38 test.pathEqual(a([[0, 1], [1, 3], [2, 1], [3, 3]]), "M1,3C1.333333,3,1.666667,1,2,1L2,0C1.666667,0,1.333333,0,1,0Z");
39 test.end();
40 });
41
42 tape("area.curve(curveCardinalOpen) uses a default tension of zero", function(test) {
43 var a = shape.area().curve(shape.curveCardinalOpen.tension(0));
44 test.equal(shape.area().curve(shape.curveCardinalOpen)([[0, 1], [1, 3], [2, 1], [3, 3]]), a([[0, 1], [1, 3], [2, 1], [3, 3]]));
45 test.end();
46 });
47
48 tape("area.curve(curveCardinalOpen.tension(tension)) uses the specified tension", function(test) {
49 test.pathEqual(shape.area().curve(shape.curveCardinalOpen.tension(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), "M1,3C1.166667,3,1.833333,1,2,1L2,0C1.833333,0,1.166667,0,1,0Z");
50 test.end();
51 });
52
53 tape("area.curve(curveCardinalOpen.tension(tension)) coerces the specified tension to a number", function(test) {
54 var a = shape.area().curve(shape.curveCardinalOpen.tension("0.5"));
55 test.equal(shape.area().curve(shape.curveCardinalOpen.tension(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), a([[0, 1], [1, 3], [2, 1], [3, 3]]));
56 test.end();
57 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveCatmullRom)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveCatmullRom);
7 test.equal(l([]), null);
8 test.pathEqual(l([[0, 1]]), "M0,1Z");
9 test.pathEqual(l([[0, 1], [1, 3]]), "M0,1L1,3");
10 test.pathEqual(l([[0, 1], [1, 3], [2, 1]]), "M0,1C0,1,0.666667,3,1,3C1.333333,3,2,1,2,1");
11 test.pathEqual(l([[0, 1], [1, 3], [2, 1], [3, 3]]), "M0,1C0,1,0.666667,3,1,3C1.333333,3,1.666667,1,2,1C2.333333,1,3,3,3,3");
12 test.end();
13 });
14
15 tape("line.curve(curveCatmullRom.alpha(1))(data) generates the expected path", function(test) {
16 var l = shape.line().curve(shape.curveCatmullRom.alpha(1));
17 test.equal(l([]), null);
18 test.pathEqual(l([[0, 1]]), "M0,1Z");
19 test.pathEqual(l([[0, 1], [1, 3]]), "M0,1L1,3");
20 test.pathEqual(l([[0, 1], [1, 3], [2, 1]]), "M0,1C0,1,0.666667,3,1,3C1.333333,3,2,1,2,1");
21 test.pathEqual(l([[0, 1], [1, 3], [2, 1], [3, 3]]), "M0,1C0,1,0.666667,3,1,3C1.333333,3,1.666667,1,2,1C2.333333,1,3,3,3,3");
22 test.end();
23 });
24
25 tape("line.curve(curveCatmullRom) uses a default alpha of 0.5 (centripetal)", function(test) {
26 var l = shape.line().curve(shape.curveCatmullRom.alpha(0.5));
27 test.equal(shape.line().curve(shape.curveCatmullRom)([[0, 1], [1, 3], [2, 1], [3, 3]]), l([[0, 1], [1, 3], [2, 1], [3, 3]]));
28 test.end();
29 });
30
31 tape("line.curve(curveCatmullRom.alpha(alpha)) coerces the specified alpha to a number", function(test) {
32 var l = shape.line().curve(shape.curveCatmullRom.alpha("0.5"));
33 test.equal(shape.line().curve(shape.curveCatmullRom.alpha(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), l([[0, 1], [1, 3], [2, 1], [3, 3]]));
34 test.end();
35 });
36
37 tape("area.curve(curveCatmullRom.alpha(0))(data) generates the expected path", function(test) {
38 var a = shape.area().curve(shape.curveCatmullRom.alpha(0));
39 test.equal(a([]), null);
40 test.pathEqual(a([[0, 1]]), "M0,1L0,0Z");
41 test.pathEqual(a([[0, 1], [1, 3]]), "M0,1L1,3L1,0L0,0Z");
42 test.pathEqual(a([[0, 1], [1, 3], [2, 1]]), "M0,1C0,1,0.666667,3,1,3C1.333333,3,2,1,2,1L2,0C2,0,1.333333,0,1,0C0.666667,0,0,0,0,0Z");
43 test.pathEqual(a([[0, 1], [1, 3], [2, 1], [3, 3]]), "M0,1C0,1,0.666667,3,1,3C1.333333,3,1.666667,1,2,1C2.333333,1,3,3,3,3L3,0C3,0,2.333333,0,2,0C1.666667,0,1.333333,0,1,0C0.666667,0,0,0,0,0Z");
44 test.end();
45 });
46
47 tape("area.curve(curveCatmullRom) uses a default alpha of 0.5 (centripetal)", function(test) {
48 var a = shape.area().curve(shape.curveCatmullRom.alpha(0.5));
49 test.equal(shape.area().curve(shape.curveCatmullRom)([[0, 1], [1, 3], [2, 1], [3, 3]]), a([[0, 1], [1, 3], [2, 1], [3, 3]]));
50 test.end();
51 });
52
53 tape("area.curve(curveCatmullRom.alpha(alpha)) coerces the specified alpha to a number", function(test) {
54 var a = shape.area().curve(shape.curveCatmullRom.alpha("0.5"));
55 test.equal(shape.area().curve(shape.curveCatmullRom.alpha(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), a([[0, 1], [1, 3], [2, 1], [3, 3]]));
56 test.end();
57 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveCatmullRomClosed)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveCatmullRomClosed);
7 test.equal(l([]), null);
8 test.pathEqual(l([[0, 1]]), "M0,1Z");
9 test.pathEqual(l([[0, 1], [1, 3]]), "M1,3L0,1Z");
10 test.pathEqual(l([[0, 1], [1, 3], [2, 1]]), "M1,3C1.333333,3,2.200267,1.324038,2,1C1.810600,0.693544,0.189400,0.693544,0,1C-0.200267,1.324038,0.666667,3,1,3");
11 test.pathEqual(l([[0, 1], [1, 3], [2, 1], [3, 3]]), "M1,3C1.333333,3,1.666667,1,2,1C2.333333,1,3.160469,2.858341,3,3C2.796233,3.179882,0.203767,0.820118,0,1C-0.160469,1.141659,0.666667,3,1,3");
12 test.end();
13 });
14
15 tape("line.curve(curveCatmullRomClosed.alpha(0))(data) generates the expected path", function(test) {
16 var l = shape.line().curve(shape.curveCatmullRomClosed.alpha(0));
17 test.equal(l([]), null);
18 test.pathEqual(l([[0, 1]]), "M0,1Z");
19 test.pathEqual(l([[0, 1], [1, 3]]), "M1,3L0,1Z");
20 test.pathEqual(l([[0, 1], [1, 3], [2, 1]]), "M1,3C1.333333,3,2.166667,1.333333,2,1C1.833333,0.666667,0.166667,0.666667,0,1C-0.166667,1.333333,0.666667,3,1,3");
21 test.pathEqual(l([[0, 1], [1, 3], [2, 1], [3, 3]]), "M1,3C1.333333,3,1.666667,1,2,1C2.333333,1,3.333333,3,3,3C2.666667,3,0.333333,1,0,1C-0.333333,1,0.666667,3,1,3");
22 test.end();
23 });
24
25 tape("line.curve(curveCatmullRomClosed.alpha(1))(data) generates the expected path", function(test) {
26 var l = shape.line().curve(shape.curveCatmullRomClosed.alpha(1));
27 test.equal(l([]), null);
28 test.pathEqual(l([[0, 1]]), "M0,1Z");
29 test.pathEqual(l([[0, 1], [1, 3]]), "M1,3L0,1Z");
30 test.pathEqual(l([[0, 1], [1, 3], [2, 1]]), "M1,3C1.333333,3,2.236068,1.314757,2,1C1.788854,0.718473,0.211146,0.718473,0,1C-0.236068,1.314757,0.666667,3,1,3");
31 test.pathEqual(l([[0, 1], [1, 3], [2, 1], [3, 3]]), "M1,3C1.333333,3,1.666667,1,2,1C2.333333,1,3.031652,2.746782,3,3C2.948962,3.408301,0.051038,0.591699,0,1C-0.031652,1.253218,0.666667,3,1,3");
32 test.end();
33 });
34
35 tape("line.curve(curveCatmullRomClosed) uses a default alpha of 0.5 (centripetal)", function(test) {
36 var l = shape.line().curve(shape.curveCatmullRomClosed.alpha(0.5));
37 test.equal(shape.line().curve(shape.curveCatmullRomClosed)([[0, 1], [1, 3], [2, 1], [3, 3]]), l([[0, 1], [1, 3], [2, 1], [3, 3]]));
38 test.end();
39 });
40
41 tape("line.curve(curveCatmullRomClosed.alpha(alpha)) coerces the specified alpha to a number", function(test) {
42 var l = shape.line().curve(shape.curveCatmullRomClosed.alpha("0.5"));
43 test.equal(shape.line().curve(shape.curveCatmullRomClosed.alpha(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), l([[0, 1], [1, 3], [2, 1], [3, 3]]));
44 test.end();
45 });
46
47 tape("area.curve(curveCatmullRomClosed.alpha(alpha)) coerces the specified alpha to a number", function(test) {
48 var a = shape.area().curve(shape.curveCatmullRomClosed.alpha("0.5"));
49 test.equal(shape.area().curve(shape.curveCatmullRomClosed.alpha(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), a([[0, 1], [1, 3], [2, 1], [3, 3]]));
50 test.end();
51 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveCatmullRomOpen)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveCatmullRomOpen);
7 test.equal(l([]), null);
8 test.equal(l([[0, 1]]), null);
9 test.equal(l([[0, 1], [1, 3]]), null);
10 test.pathEqual(l([[0, 1], [1, 3], [2, 1]]), "M1,3Z");
11 test.pathEqual(l([[0, 1], [1, 3], [2, 1], [3, 3]]), "M1,3C1.333333,3,1.666667,1,2,1");
12 test.end();
13 });
14
15 tape("line.curve(curveCatmullRomOpen.alpha(1))(data) generates the expected path", function(test) {
16 var l = shape.line().curve(shape.curveCatmullRomOpen.alpha(1));
17 test.equal(l([]), null);
18 test.equal(l([[0, 1]]), null);
19 test.equal(l([[0, 1], [1, 3]]), null);
20 test.pathEqual(l([[0, 1], [1, 3], [2, 1]]), "M1,3Z");
21 test.pathEqual(l([[0, 1], [1, 3], [2, 1], [3, 3]]), "M1,3C1.333333,3,1.666667,1,2,1");
22 test.end();
23 });
24
25 tape("line.curve(curveCatmullRomOpen) uses a default alpha of 0.5 (centripetal)", function(test) {
26 var l = shape.line().curve(shape.curveCatmullRomOpen.alpha(0.5));
27 test.equal(shape.line().curve(shape.curveCatmullRomOpen)([[0, 1], [1, 3], [2, 1], [3, 3]]), l([[0, 1], [1, 3], [2, 1], [3, 3]]));
28 test.end();
29 });
30
31 tape("line.curve(curveCatmullRom.alpha(alpha)) coerces the specified alpha to a number", function(test) {
32 var l = shape.line().curve(shape.curveCatmullRom.alpha("0.5"));
33 test.equal(shape.line().curve(shape.curveCatmullRom.alpha(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), l([[0, 1], [1, 3], [2, 1], [3, 3]]));
34 test.end();
35 });
36
37 tape("area.curve(curveCatmullRomOpen.alpha(0.5))(data) generates the expected path", function(test) {
38 var a = shape.area().curve(shape.curveCatmullRomOpen, 0.5);
39 test.equal(a([]), null);
40 test.equal(a([[0, 1]]), null);
41 test.equal(a([[0, 1], [1, 3]]), null);
42 test.pathEqual(a([[0, 1], [1, 3], [2, 1]]), "M1,3L1,0Z");
43 test.pathEqual(a([[0, 1], [1, 3], [2, 1], [3, 3]]), "M1,3C1.333333,3,1.666667,1,2,1L2,0C1.666667,0,1.333333,0,1,0Z");
44 test.end();
45 });
46
47 tape("area.curve(curveCatmullRomOpen) uses a default alpha of 0.5 (centripetal)", function(test) {
48 var a = shape.area().curve(shape.curveCatmullRomOpen, 0.5);
49 test.equal(shape.area().curve(shape.curveCatmullRomOpen)([[0, 1], [1, 3], [2, 1], [3, 3]]), a([[0, 1], [1, 3], [2, 1], [3, 3]]));
50 test.end();
51 });
52
53 tape("area.curve(curveCatmullRomOpen.alpha(alpha)) coerces the specified alpha to a number", function(test) {
54 var a = shape.area().curve(shape.curveCatmullRomOpen.alpha("0.5"));
55 test.equal(shape.area().curve(shape.curveCatmullRomOpen.alpha(0.5))([[0, 1], [1, 3], [2, 1], [3, 3]]), a([[0, 1], [1, 3], [2, 1], [3, 3]]));
56 test.end();
57 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveLinear)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveLinear);
7 test.equal(l([]), null);
8 test.pathEqual(l([[0, 1]]), "M0,1Z");
9 test.pathEqual(l([[0, 1], [2, 3]]), "M0,1L2,3");
10 test.pathEqual(l([[0, 1], [2, 3], [4, 5]]), "M0,1L2,3L4,5");
11 test.end();
12 });
13
14 tape("area.curve(curveLinear)(data) generates the expected path", function(test) {
15 var a = shape.area().curve(shape.curveLinear);
16 test.equal(a([]), null);
17 test.pathEqual(a([[0, 1]]), "M0,1L0,0Z");
18 test.pathEqual(a([[0, 1], [2, 3]]), "M0,1L2,3L2,0L0,0Z");
19 test.pathEqual(a([[0, 1], [2, 3], [4, 5]]), "M0,1L2,3L4,5L4,0L2,0L0,0Z");
20 test.end();
21 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveLinearClosed)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveLinearClosed);
7 test.equal(l([]), null);
8 test.pathEqual(l([[0, 1]]), "M0,1Z");
9 test.pathEqual(l([[0, 1], [2, 3]]), "M0,1L2,3Z");
10 test.pathEqual(l([[0, 1], [2, 3], [4, 5]]), "M0,1L2,3L4,5Z");
11 test.end();
12 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveMonotoneX)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveMonotoneX);
7 test.equal(l([]), null);
8 test.pathEqual(l([[0, 1]]), "M0,1Z");
9 test.pathEqual(l([[0, 1], [1, 3]]), "M0,1L1,3");
10 test.pathEqual(l([[0, 1], [1, 3], [2, 1]]), "M0,1C0.333333,2,0.666667,3,1,3C1.333333,3,1.666667,2,2,1");
11 test.pathEqual(l([[0, 1], [1, 3], [2, 1], [3, 3]]), "M0,1C0.333333,2,0.666667,3,1,3C1.333333,3,1.666667,1,2,1C2.333333,1,2.666667,2,3,3");
12 test.end();
13 });
14
15 tape("line.curve(curveMonotoneX)(data) preserves monotonicity in y", function(test) {
16 var l = shape.line().curve(shape.curveMonotoneX);
17 test.pathEqual(l([[0, 200], [100, 100], [200, 100], [300, 300], [400, 300]]), "M0,200C33.333333,150,66.666667,100,100,100C133.333333,100,166.666667,100,200,100C233.333333,100,266.666667,300,300,300C333.333333,300,366.666667,300,400,300");
18 test.end();
19 });
20
21 tape("line.curve(curveMonotoneX)(data) handles duplicate x-values", function(test) {
22 var l = shape.line().curve(shape.curveMonotoneX);
23 test.pathEqual(l([[0, 200], [0, 100], [100, 100], [200, 0]]), "M0,200C0,200,0,100,0,100C33.333333,100,66.666667,100,100,100C133.333333,100,166.666667,50,200,0");
24 test.pathEqual(l([[0, 200], [100, 100], [100, 0], [200, 0]]), "M0,200C33.333333,183.333333,66.666667,166.666667,100,100C100,100,100,0,100,0C133.333333,0,166.666667,0,200,0");
25 test.pathEqual(l([[0, 200], [100, 100], [200, 100], [200, 0]]), "M0,200C33.333333,150,66.666667,100,100,100C133.333333,100,166.666667,100,200,100C200,100,200,0,200,0");
26 test.end();
27 });
28
29 tape("line.curve(curveMonotoneX)(data) handles segments of infinite slope", function(test) {
30 var l = shape.line().curve(shape.curveMonotoneX);
31 test.pathEqual(l([[0, 200], [100, 150], [100, 50], [200, 0]]), "M0,200C33.333333,191.666667,66.666667,183.333333,100,150C100,150,100,50,100,50C133.333333,16.666667,166.666667,8.333333,200,0");
32 test.pathEqual(l([[200, 0], [100, 50], [100, 150], [0, 200]]), "M200,0C166.666667,8.333333,133.333333,16.666667,100,50C100,50,100,150,100,150C66.666667,183.333333,33.333333,191.666667,0,200");
33 test.end();
34 });
35
36 tape("line.curve(curveMonotoneX)(data) ignores coincident points", function(test) {
37 var l = shape.line().curve(shape.curveMonotoneX),
38 p = l([[0, 200], [50, 200], [100, 100], [150, 0], [200, 0]]);
39 test.equal(l([[0, 200], [0, 200], [50, 200], [100, 100], [150, 0], [200, 0]]), p);
40 test.equal(l([[0, 200], [50, 200], [50, 200], [100, 100], [150, 0], [200, 0]]), p);
41 test.equal(l([[0, 200], [50, 200], [100, 100], [100, 100], [150, 0], [200, 0]]), p);
42 test.equal(l([[0, 200], [50, 200], [100, 100], [150, 0], [150, 0], [200, 0]]), p);
43 test.equal(l([[0, 200], [50, 200], [100, 100], [150, 0], [200, 0], [200, 0]]), p);
44 test.end();
45 });
46
47 tape("area.curve(curveMonotoneX)(data) generates the expected path", function(test) {
48 var a = shape.area().curve(shape.curveMonotoneX);
49 test.equal(a([]), null);
50 test.pathEqual(a([[0, 1]]), "M0,1L0,0Z");
51 test.pathEqual(a([[0, 1], [1, 3]]), "M0,1L1,3L1,0L0,0Z");
52 test.pathEqual(a([[0, 1], [1, 3], [2, 1]]), "M0,1C0.333333,2,0.666667,3,1,3C1.333333,3,1.666667,2,2,1L2,0C1.666667,0,1.333333,0,1,0C0.666667,0,0.333333,0,0,0Z");
53 test.pathEqual(a([[0, 1], [1, 3], [2, 1], [3, 3]]), "M0,1C0.333333,2,0.666667,3,1,3C1.333333,3,1.666667,1,2,1C2.333333,1,2.666667,2,3,3L3,0C2.666667,0,2.333333,0,2,0C1.666667,0,1.333333,0,1,0C0.666667,0,0.333333,0,0,0Z");
54 test.end();
55 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveMonotoneY)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveMonotoneY);
7 test.equal(l([]), null);
8 test.pathEqual(l([[0, 1]].map(reflect)), "M1,0Z");
9 test.pathEqual(l([[0, 1], [1, 3]].map(reflect)), "M1,0L3,1");
10 test.pathEqual(l([[0, 1], [1, 3], [2, 1]].map(reflect)), "M1,0C2,0.333333,3,0.666667,3,1C3,1.333333,2,1.666667,1,2");
11 test.pathEqual(l([[0, 1], [1, 3], [2, 1], [3, 3]].map(reflect)), "M1,0C2,0.333333,3,0.666667,3,1C3,1.333333,1,1.666667,1,2C1,2.333333,2,2.666667,3,3");
12 test.end();
13 });
14
15 tape("line.curve(curveMonotoneY)(data) preserves monotonicity in y", function(test) {
16 var l = shape.line().curve(shape.curveMonotoneY);
17 test.pathEqual(l([[0, 200], [100, 100], [200, 100], [300, 300], [400, 300]].map(reflect)), "M200,0C150,33.333333,100,66.666667,100,100C100,133.333333,100,166.666667,100,200C100,233.333333,300,266.666667,300,300C300,333.333333,300,366.666667,300,400");
18 test.end();
19 });
20
21 tape("line.curve(curveMonotoneY)(data) handles duplicate x-values", function(test) {
22 var l = shape.line().curve(shape.curveMonotoneY);
23 test.pathEqual(l([[0, 200], [0, 100], [100, 100], [200, 0]].map(reflect)), "M200,0C200,0,100,0,100,0C100,33.333333,100,66.666667,100,100C100,133.333333,50,166.666667,0,200");
24 test.pathEqual(l([[0, 200], [100, 100], [100, 0], [200, 0]].map(reflect)), "M200,0C183.333333,33.333333,166.666667,66.666667,100,100C100,100,0,100,0,100C0,133.333333,0,166.666667,0,200");
25 test.pathEqual(l([[0, 200], [100, 100], [200, 100], [200, 0]].map(reflect)), "M200,0C150,33.333333,100,66.666667,100,100C100,133.333333,100,166.666667,100,200C100,200,0,200,0,200");
26 test.end();
27 });
28
29 tape("line.curve(curveMonotoneY)(data) handles segments of infinite slope", function(test) {
30 var l = shape.line().curve(shape.curveMonotoneY);
31 test.pathEqual(l([[0, 200], [100, 150], [100, 50], [200, 0]].map(reflect)), "M200,0C191.666667,33.333333,183.333333,66.666667,150,100C150,100,50,100,50,100C16.666667,133.333333,8.333333,166.666667,0,200");
32 test.pathEqual(l([[200, 0], [100, 50], [100, 150], [0, 200]].map(reflect)), "M0,200C8.333333,166.666667,16.666667,133.333333,50,100C50,100,150,100,150,100C183.333333,66.666667,191.666667,33.333333,200,0");
33 test.end();
34 });
35
36 tape("line.curve(curveMonotoneY)(data) ignores coincident points", function(test) {
37 var l = shape.line().curve(shape.curveMonotoneY),
38 p = l([[0, 200], [50, 200], [100, 100], [150, 0], [200, 0]].map(reflect));
39 test.equal(l([[0, 200], [0, 200], [50, 200], [100, 100], [150, 0], [200, 0]].map(reflect)), p);
40 test.equal(l([[0, 200], [50, 200], [50, 200], [100, 100], [150, 0], [200, 0]].map(reflect)), p);
41 test.equal(l([[0, 200], [50, 200], [100, 100], [100, 100], [150, 0], [200, 0]].map(reflect)), p);
42 test.equal(l([[0, 200], [50, 200], [100, 100], [150, 0], [150, 0], [200, 0]].map(reflect)), p);
43 test.equal(l([[0, 200], [50, 200], [100, 100], [150, 0], [200, 0], [200, 0]].map(reflect)), p);
44 test.end();
45 });
46
47 tape("area.curve(curveMonotoneY)(data) generates the expected path", function(test) {
48 var a = shape.area().curve(shape.curveMonotoneY);
49 test.equal(a([].map(reflect)), null);
50 test.pathEqual(a([[0, 1]].map(reflect)), "M1,0L1,0Z");
51 test.pathEqual(a([[0, 1], [1, 3]].map(reflect)), "M1,0L3,1L3,0L1,0Z");
52 test.pathEqual(a([[0, 1], [1, 3], [2, 1]].map(reflect)), "M1,0C2,0.333333,3,0.666667,3,1C3,1.333333,2,1.666667,1,2L1,0C1,0,3,0,3,0C3,0,1,0,1,0Z");
53 test.pathEqual(a([[0, 1], [1, 3], [2, 1], [3, 3]].map(reflect)), "M1,0C2,0.333333,3,0.666667,3,1C3,1.333333,1,1.666667,1,2C1,2.333333,2,2.666667,3,3L3,0C3,0,1,0,1,0C1,0,3,0,3,0C3,0,1,0,1,0Z");
54 test.end();
55 });
56
57 function reflect(p) {
58 return [p[1], p[0]];
59 }
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveNatural)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveNatural);
7 test.equal(l([]), null);
8 test.pathEqual(l([[0, 1]]), "M0,1Z");
9 test.pathEqual(l([[0, 1], [1, 3]]), "M0,1L1,3");
10 test.pathEqual(l([[0, 1], [1, 3], [2, 1]]), "M0,1C0.333333,2,0.666667,3,1,3C1.333333,3,1.666667,2,2,1");
11 test.pathEqual(l([[0, 1], [1, 3], [2, 1], [3, 3]]), "M0,1C0.333333,2.111111,0.666667,3.222222,1,3C1.333333,2.777778,1.666667,1.222222,2,1C2.333333,0.777778,2.666667,1.888889,3,3");
12 test.end();
13 });
14
15 tape("area.curve(curveNatural)(data) generates the expected path", function(test) {
16 var a = shape.area().curve(shape.curveNatural);
17 test.equal(a([]), null);
18 test.pathEqual(a([[0, 1]]), "M0,1L0,0Z");
19 test.pathEqual(a([[0, 1], [1, 3]]), "M0,1L1,3L1,0L0,0Z");
20 test.pathEqual(a([[0, 1], [1, 3], [2, 1]]), "M0,1C0.333333,2,0.666667,3,1,3C1.333333,3,1.666667,2,2,1L2,0C1.666667,0,1.333333,0,1,0C0.666667,0,0.333333,0,0,0Z");
21 test.pathEqual(a([[0, 1], [1, 3], [2, 1], [3, 3]]), "M0,1C0.333333,2.111111,0.666667,3.222222,1,3C1.333333,2.777778,1.666667,1.222222,2,1C2.333333,0.777778,2.666667,1.888889,3,3L3,0C2.666667,0,2.333333,0,2,0C1.666667,0,1.333333,0,1,0C0.666667,0,0.333333,0,0,0Z");
22 test.end();
23 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveStep)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveStep);
7 test.equal(l([]), null);
8 test.pathEqual(l([[0, 1]]), "M0,1Z");
9 test.pathEqual(l([[0, 1], [2, 3]]), "M0,1L1,1L1,3L2,3");
10 test.pathEqual(l([[0, 1], [2, 3], [4, 5]]), "M0,1L1,1L1,3L3,3L3,5L4,5");
11 test.end();
12 });
13
14 tape("area.curve(curveStep)(data) generates the expected path", function(test) {
15 var a = shape.area().curve(shape.curveStep);
16 test.equal(a([]), null);
17 test.pathEqual(a([[0, 1]]), "M0,1L0,0Z");
18 test.pathEqual(a([[0, 1], [2, 3]]), "M0,1L1,1L1,3L2,3L2,0L1,0L1,0L0,0Z");
19 test.pathEqual(a([[0, 1], [2, 3], [4, 5]]), "M0,1L1,1L1,3L3,3L3,5L4,5L4,0L3,0L3,0L1,0L1,0L0,0Z");
20 test.end();
21 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveStepAfter)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveStepAfter);
7 test.equal(l([]), null);
8 test.pathEqual(l([[0, 1]]), "M0,1Z");
9 test.pathEqual(l([[0, 1], [2, 3]]), "M0,1L2,1L2,3");
10 test.pathEqual(l([[0, 1], [2, 3], [4, 5]]), "M0,1L2,1L2,3L4,3L4,5");
11 test.end();
12 });
13
14 tape("area.curve(curveStepAfter)(data) generates the expected path", function(test) {
15 var a = shape.area().curve(shape.curveStepAfter);
16 test.equal(a([]), null);
17 test.pathEqual(a([[0, 1]]), "M0,1L0,0Z");
18 test.pathEqual(a([[0, 1], [2, 3]]), "M0,1L2,1L2,3L2,0L2,0L0,0Z");
19 test.pathEqual(a([[0, 1], [2, 3], [4, 5]]), "M0,1L2,1L2,3L4,3L4,5L4,0L4,0L2,0L2,0L0,0Z");
20 test.end();
21 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 require("../pathEqual");
4
5 tape("line.curve(curveStepBefore)(data) generates the expected path", function(test) {
6 var l = shape.line().curve(shape.curveStepBefore);
7 test.equal(l([]), null);
8 test.pathEqual(l([[0, 1]]), "M0,1Z");
9 test.pathEqual(l([[0, 1], [2, 3]]), "M0,1L0,3L2,3");
10 test.pathEqual(l([[0, 1], [2, 3], [4, 5]]), "M0,1L0,3L2,3L2,5L4,5");
11 test.end();
12 });
13
14 tape("area.curve(curveStepBefore)(data) generates the expected path", function(test) {
15 var a = shape.area().curve(shape.curveStepBefore);
16 test.equal(a([]), null);
17 test.pathEqual(a([[0, 1]]), "M0,1L0,0Z");
18 test.pathEqual(a([[0, 1], [2, 3]]), "M0,1L0,3L2,3L2,0L0,0L0,0Z");
19 test.pathEqual(a([[0, 1], [2, 3], [4, 5]]), "M0,1L0,3L2,3L2,5L4,5L4,0L2,0L2,0L0,0L0,0Z");
20 test.end();
21 });
0 var tape = require("tape");
1
2 tape.Test.prototype.inDelta = function(actual, expected) {
3 this._assert(expected - 1e-6 < actual && actual < expected + 1e-6, {
4 message: "should be in delta",
5 operator: "inDelta",
6 actual: actual,
7 expected: expected
8 });
9 };
0 var tape = require("tape"),
1 shape = require("../");
2
3 require("./pathEqual");
4
5 tape("line() returns a default line shape", function(test) {
6 var l = shape.line();
7 test.equal(l.x()([42, 34]), 42);
8 test.equal(l.y()([42, 34]), 34);
9 test.equal(l.defined()([42, 34]), true);
10 test.equal(l.curve(), shape.curveLinear);
11 test.equal(l.context(), null);
12 test.pathEqual(l([[0, 1], [2, 3], [4, 5]]), "M0,1L2,3L4,5");
13 test.end();
14 });
15
16 tape("line.x(f)(data) passes d, i and data to the specified function f", function(test) {
17 var data = ["a", "b"], actual = [];
18 shape.line().x(function() { actual.push([].slice.call(arguments)); })(data);
19 test.deepEqual(actual, [["a", 0, data], ["b", 1, data]]);
20 test.end();
21 });
22
23 tape("line.y(f)(data) passes d, i and data to the specified function f", function(test) {
24 var data = ["a", "b"], actual = [];
25 shape.line().y(function() { actual.push([].slice.call(arguments)); })(data);
26 test.deepEqual(actual, [["a", 0, data], ["b", 1, data]]);
27 test.end();
28 });
29
30 tape("line.defined(f)(data) passes d, i and data to the specified function f", function(test) {
31 var data = ["a", "b"], actual = [];
32 shape.line().defined(function() { actual.push([].slice.call(arguments)); })(data);
33 test.deepEqual(actual, [["a", 0, data], ["b", 1, data]]);
34 test.end();
35 });
36
37 tape("line.x(x)(data) observes the specified function", function(test) {
38 var l = shape.line().x(function(d) { return d.x; });
39 test.pathEqual(l([{x: 0, 1: 1}, {x: 2, 1: 3}, {x: 4, 1: 5}]), "M0,1L2,3L4,5");
40 test.end();
41 });
42
43 tape("line.x(x)(data) observes the specified constant", function(test) {
44 var l = shape.line().x(0);
45 test.pathEqual(l([{1: 1}, {1: 3}, {1: 5}]), "M0,1L0,3L0,5");
46 test.end();
47 });
48
49 tape("line.y(y)(data) observes the specified function", function(test) {
50 var l = shape.line().y(function(d) { return d.y; });
51 test.pathEqual(l([{0: 0, y: 1}, {0: 2, y: 3}, {0: 4, y: 5}]), "M0,1L2,3L4,5");
52 test.end();
53 });
54
55 tape("line.y(y)(data) observes the specified constant", function(test) {
56 var l = shape.line().y(0);
57 test.pathEqual(l([{0: 0}, {0: 2}, {0: 4}]), "M0,0L2,0L4,0");
58 test.end();
59 });
60
61 tape("line.curve(curve) sets the curve method", function(test) {
62 var l = shape.line().curve(shape.curveLinearClosed);
63 test.equal(l([]), null);
64 test.pathEqual(l([[0, 1], [2, 3]]), "M0,1L2,3Z");
65 test.end();
66 });
0 var tape = require("tape"),
1 shape = require("../");
2
3 require("./pathEqual");
4
5 tape("lineRadial() returns a default radial line shape", function(test) {
6 var l = shape.lineRadial();
7 test.equal(l.angle()([42, 34]), 42);
8 test.equal(l.radius()([42, 34]), 34);
9 test.equal(l.defined()([42, 34]), true);
10 test.equal(l.curve(), shape.curveLinear);
11 test.equal(l.context(), null);
12 test.pathEqual(l([[0, 1], [2, 3], [4, 5]]), "M0,-1L2.727892,1.248441L-3.784012,3.268218");
13 test.end();
14 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 tape("stackOffsetDiverging(series, order) applies a zero baseline, ignoring existing offsets", function(test) {
4 var series = [
5 [[1, 2], [2, 4], [3, 4]],
6 [[0, 3], [0, 4], [0, 2]],
7 [[0, 5], [0, 2], [0, 4]]
8 ];
9 shape.stackOffsetDiverging(series, shape.stackOrderNone(series));
10 test.deepEqual(series, [
11 [[0, 1], [0, 2], [0, 1]],
12 [[1, 4], [2, 6], [1, 3]],
13 [[4, 9], [6, 8], [3, 7]]
14 ]);
15 test.end();
16 });
17
18 tape("stackOffsetDiverging(series, order) treats NaN as zero", function(test) {
19 var series = [
20 [[0, 1], [0, 2], [0, 1]],
21 [[0, 3], [0, NaN], [0, 2]],
22 [[0, 5], [0, 2], [0, 4]]
23 ];
24 shape.stackOffsetDiverging(series, shape.stackOrderNone(series));
25 test.ok(isNaN(series[1][1][1]));
26 series[1][1][1] = "NaN"; // can’t test.equal NaN
27 test.deepEqual(series, [
28 [[0, 1], [0, 2], [0, 1]],
29 [[1, 4], [2, "NaN"], [1, 3]],
30 [[4, 9], [2, 4], [3, 7]]
31 ]);
32 test.end();
33 });
34
35 tape("stackOffsetDiverging(series, order) observes the specified order", function(test) {
36 var series = [
37 [[0, 1], [0, 2], [0, 1]],
38 [[0, 3], [0, 4], [0, 2]],
39 [[0, 5], [0, 2], [0, 4]]
40 ];
41 shape.stackOffsetDiverging(series, shape.stackOrderReverse(series));
42 test.deepEqual(series, [
43 [[8, 9], [6, 8], [6, 7]],
44 [[5, 8], [2, 6], [4, 6]],
45 [[0, 5], [0, 2], [0, 4]]
46 ]);
47 test.end();
48 });
49
50 tape("stackOffsetDiverging(series, order) puts negative values below zero, in order", function(test) {
51 var series = [
52 [[0, 1], [0, -2], [0, -1]],
53 [[0, -3], [0, -4], [0, -2]],
54 [[0, -5], [0, -2], [0, 4]]
55 ];
56 shape.stackOffsetDiverging(series, shape.stackOrderNone(series));
57 test.deepEqual(series, [
58 [[ 0, 1], [-2, 0], [-1, 0]],
59 [[-3, 0], [-6, -2], [-3, -1]],
60 [[-8, -3], [-8, -6], [ 0, 4]]
61 ]);
62 test.end();
63 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 tape("stackOffsetExpand(series, order) expands to fill [0, 1]", function(test) {
4 var series = [
5 [[0, 1], [0, 2], [0, 1]],
6 [[0, 3], [0, 4], [0, 2]],
7 [[0, 5], [0, 2], [0, 4]]
8 ];
9 shape.stackOffsetExpand(series, shape.stackOrderNone(series));
10 test.deepEqual(series, [
11 [[0 / 9, 1 / 9], [0 / 8, 2 / 8], [0 / 7, 1 / 7]],
12 [[1 / 9, 4 / 9], [2 / 8, 6 / 8], [1 / 7, 3 / 7]],
13 [[4 / 9, 9 / 9], [6 / 8, 8 / 8], [3 / 7, 7 / 7]]
14 ]);
15 test.end();
16 });
17
18 tape("stackOffsetExpand(series, order) treats NaN as zero", function(test) {
19 var series = [
20 [[0, 1], [0, 2], [0, 1]],
21 [[0, 3], [0, NaN], [0, 2]],
22 [[0, 5], [0, 2], [0, 4]]
23 ];
24 shape.stackOffsetExpand(series, shape.stackOrderNone(series));
25 test.ok(isNaN(series[1][1][1]));
26 series[1][1][1] = "NaN"; // can’t test.equal NaN
27 test.deepEqual(series, [
28 [[0 / 9, 1 / 9], [0 / 4, 2 / 4], [0 / 7, 1 / 7]],
29 [[1 / 9, 4 / 9], [2 / 4, "NaN"], [1 / 7, 3 / 7]],
30 [[4 / 9, 9 / 9], [2 / 4, 4 / 4], [3 / 7, 7 / 7]]
31 ]);
32 test.end();
33 });
34
35 tape("stackOffsetExpand(series, order) observes the specified order", function(test) {
36 var series = [
37 [[0, 1], [0, 2], [0, 1]],
38 [[0, 3], [0, 4], [0, 2]],
39 [[0, 5], [0, 2], [0, 4]]
40 ];
41 shape.stackOffsetExpand(series, shape.stackOrderReverse(series));
42 test.deepEqual(series, [
43 [[8 / 9, 9 / 9], [6 / 8, 8 / 8], [6 / 7, 7 / 7]],
44 [[5 / 9, 8 / 9], [2 / 8, 6 / 8], [4 / 7, 6 / 7]],
45 [[0 / 9, 5 / 9], [0 / 8, 2 / 8], [0 / 7, 4 / 7]]
46 ]);
47 test.end();
48 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 tape("stackOffsetNone(series, order) stacks upon the first layer’s existing positions", function(test) {
4 var series = [
5 [[1, 2], [2, 4], [3, 4]],
6 [[0, 3], [0, 4], [0, 2]],
7 [[0, 5], [0, 2], [0, 4]]
8 ];
9 shape.stackOffsetNone(series, shape.stackOrderNone(series));
10 test.deepEqual(series, [
11 [[1, 2], [2, 4], [3, 4]],
12 [[2, 5], [4, 8], [4, 6]],
13 [[5, 10], [8, 10], [6, 10]]
14 ]);
15 test.end();
16 });
17
18 tape("stackOffsetNone(series, order) treats NaN as zero", function(test) {
19 var series = [
20 [[0, 1], [0, 2], [0, 1]],
21 [[0, 3], [0, NaN], [0, 2]],
22 [[0, 5], [0, 2], [0, 4]]
23 ];
24 shape.stackOffsetNone(series, shape.stackOrderNone(series));
25 test.ok(isNaN(series[1][1][1]));
26 series[1][1][1] = "NaN"; // can’t test.equal NaN
27 test.deepEqual(series, [
28 [[0, 1], [0, 2], [0, 1]],
29 [[1, 4], [2, "NaN"], [1, 3]],
30 [[4, 9], [2, 4], [3, 7]]
31 ]);
32 test.end();
33 });
34
35 tape("stackOffsetNone(series, order) observes the specified order", function(test) {
36 var series = [
37 [[0, 1], [0, 2], [0, 1]],
38 [[0, 3], [0, 4], [0, 2]],
39 [[0, 5], [0, 2], [0, 4]]
40 ];
41 shape.stackOffsetNone(series, shape.stackOrderReverse(series));
42 test.deepEqual(series, [
43 [[8, 9], [6, 8], [6, 7]],
44 [[5, 8], [2, 6], [4, 6]],
45 [[0, 5], [0, 2], [0, 4]]
46 ]);
47 test.end();
48 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 tape("stackOffsetSilhouette(series, order) centers the stack around zero", function(test) {
4 var series = [
5 [[0, 1], [0, 2], [0, 1]],
6 [[0, 3], [0, 4], [0, 2]],
7 [[0, 5], [0, 2], [0, 4]]
8 ];
9 shape.stackOffsetSilhouette(series, shape.stackOrderNone(series));
10 test.deepEqual(series, [
11 [[0 - 9 / 2, 1 - 9 / 2], [0 - 8 / 2, 2 - 8 / 2], [0 - 7 / 2, 1 - 7 / 2]],
12 [[1 - 9 / 2, 4 - 9 / 2], [2 - 8 / 2, 6 - 8 / 2], [1 - 7 / 2, 3 - 7 / 2]],
13 [[4 - 9 / 2, 9 - 9 / 2], [6 - 8 / 2, 8 - 8 / 2], [3 - 7 / 2, 7 - 7 / 2]]
14 ]);
15 test.end();
16 });
17
18 tape("stackOffsetSilhouette(series, order) treats NaN as zero", function(test) {
19 var series = [
20 [[0, 1], [0, 2], [0, 1]],
21 [[0, 3], [0, NaN], [0, 2]],
22 [[0, 5], [0, 2], [0, 4]]
23 ];
24 shape.stackOffsetSilhouette(series, shape.stackOrderNone(series));
25 test.ok(isNaN(series[1][1][1]));
26 series[1][1][1] = "NaN"; // can’t test.equal NaN
27 test.deepEqual(series, [
28 [[0 - 9 / 2, 1 - 9 / 2], [0 - 4 / 2, 2 - 4 / 2], [0 - 7 / 2, 1 - 7 / 2]],
29 [[1 - 9 / 2, 4 - 9 / 2], [2 - 4 / 2, "NaN"], [1 - 7 / 2, 3 - 7 / 2]],
30 [[4 - 9 / 2, 9 - 9 / 2], [2 - 4 / 2, 4 - 4 / 2], [3 - 7 / 2, 7 - 7 / 2]]
31 ]);
32 test.end();
33 });
34
35 tape("stackOffsetSilhouette(series, order) observes the specified order", function(test) {
36 var series = [
37 [[0, 1], [0, 2], [0, 1]],
38 [[0, 3], [0, 4], [0, 2]],
39 [[0, 5], [0, 2], [0, 4]]
40 ];
41 shape.stackOffsetSilhouette(series, shape.stackOrderReverse(series));
42 test.deepEqual(series, [
43 [[8 - 9 / 2, 9 - 9 / 2], [6 - 8 / 2, 8 - 8 / 2], [6 - 7 / 2, 7 - 7 / 2]],
44 [[5 - 9 / 2, 8 - 9 / 2], [2 - 8 / 2, 6 - 8 / 2], [4 - 7 / 2, 6 - 7 / 2]],
45 [[0 - 9 / 2, 5 - 9 / 2], [0 - 8 / 2, 2 - 8 / 2], [0 - 7 / 2, 4 - 7 / 2]]
46 ]);
47 test.end();
48 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 tape("stackOffsetWiggle(series, order) minimizes weighted wiggle", function(test) {
4 var series = [
5 [[0, 1], [0, 2], [0, 1]],
6 [[0, 3], [0, 4], [0, 2]],
7 [[0, 5], [0, 2], [0, 4]]
8 ];
9 shape.stackOffsetWiggle(series, shape.stackOrderNone(series));
10 test.deepEqual(series.map(roundSeries), [
11 [[0, 1], [-1, 1], [0.7857143, 1.7857143]],
12 [[1, 4], [ 1, 5], [1.7857143, 3.7857143]],
13 [[4, 9], [ 5, 7], [3.7857143, 7.7857143]]
14 ].map(roundSeries));
15 test.end();
16 });
17
18 tape("stackOffsetWiggle(series, order) treats NaN as zero", function(test) {
19 var series = [
20 [[0, 1], [0, 2], [0, 1]],
21 [[0, NaN], [0, NaN], [0, NaN]],
22 [[0, 3], [0, 4], [0, 2]],
23 [[0, 5], [0, 2], [0, 4]]
24 ];
25 shape.stackOffsetWiggle(series, shape.stackOrderNone(series));
26 test.ok(isNaN(series[1][0][1]));
27 test.ok(isNaN(series[1][0][2]));
28 test.ok(isNaN(series[1][0][3]));
29 series[1][0][1] = series[1][1][1] = series[1][2][1] = "NaN"; // can’t test.equal NaN
30 test.deepEqual(series.map(roundSeries), [
31 [[0, 1], [-1, 1], [0.7857143, 1.7857143]],
32 [[1, "NaN"], [ 1, "NaN"], [1.7857143, "NaN"]],
33 [[1, 4], [ 1, 5], [1.7857143, 3.7857143]],
34 [[4, 9], [ 5, 7], [3.7857143, 7.7857143]]
35 ].map(roundSeries));
36 test.end();
37 });
38
39 tape("stackOffsetWiggle(series, order) observes the specified order", function(test) {
40 var series = [
41 [[0, 1], [0, 2], [0, 1]],
42 [[0, 3], [0, 4], [0, 2]],
43 [[0, 5], [0, 2], [0, 4]]
44 ];
45 shape.stackOffsetWiggle(series, shape.stackOrderReverse(series));
46 test.deepEqual(series.map(roundSeries), [
47 [[8, 9], [8, 10], [7.21428571, 8.21428571]],
48 [[5, 8], [4, 8], [5.21428571, 7.21428571]],
49 [[0, 5], [2, 4], [1.21428571, 5.21428571]]
50 ].map(roundSeries));
51 test.end();
52 });
53
54 function roundSeries(series) {
55 return series.map(function(point) {
56 return point.map(function(value) {
57 return isNaN(value) ? value : Math.round(value * 1e6) / 1e6;
58 });
59 });
60 }
0 var tape = require("tape"),
1 shape = require("../../");
2
3 tape("stackOrderAscending(series) returns an order by sum", function(test) {
4 test.deepEqual(shape.stackOrderAscending([
5 [[0, 1], [0, 2], [0, 3]],
6 [[0, 2], [0, 3], [0, 4]],
7 [[0, 0], [0, 1], [0, 2]]
8 ]), [2, 0, 1]);
9 test.end();
10 });
11
12 tape("stackOrderAscending(series) treats NaN values as zero", function(test) {
13 test.deepEqual(shape.stackOrderAscending([
14 [[0, 1], [0, 2], [0, NaN], [0, 3]],
15 [[0, 2], [0, 3], [0, NaN], [0, 4]],
16 [[0, 0], [0, 1], [0, NaN], [0, 2]]
17 ]), [2, 0, 1]);
18 test.end();
19 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 tape("stackOrderDescending(series) returns an order by sum", function(test) {
4 test.deepEqual(shape.stackOrderDescending([
5 [[0, 1], [0, 2], [0, 3]],
6 [[0, 2], [0, 3], [0, 4]],
7 [[0, 0], [0, 1], [0, 2]]
8 ]), [1, 0, 2]);
9 test.end();
10 });
11
12 tape("stackOrderDescending(series) treats NaN values as zero", function(test) {
13 test.deepEqual(shape.stackOrderDescending([
14 [[0, 1], [0, 2], [0, 3], [0, NaN]],
15 [[0, 2], [0, 3], [0, 4], [0, NaN]],
16 [[0, 0], [0, 1], [0, 2], [0, NaN]]
17 ]), [1, 0, 2]);
18 test.end();
19 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 tape("stackOrderInsideOut(series) returns an order by sum", function(test) {
4 test.deepEqual(shape.stackOrderInsideOut([
5 [[0, 0]],
6 [[0, 1]],
7 [[0, 2]],
8 [[0, 3]],
9 [[0, 4]],
10 [[0, 5]],
11 [[0, 6]]
12 ]), [2, 3, 6, 5, 4, 1, 0]);
13 test.end();
14 });
15
16 tape("stackOrderInsideOut(series) treats NaN values as zero", function(test) {
17 test.deepEqual(shape.stackOrderInsideOut([
18 [[0, 0], [0, NaN]],
19 [[0, 1], [0, NaN]],
20 [[0, 2], [0, NaN]],
21 [[0, 3], [0, NaN]],
22 [[0, 4], [0, NaN]],
23 [[0, 5], [0, NaN]],
24 [[0, 6], [0, NaN]]
25 ]), [2, 3, 6, 5, 4, 1, 0]);
26 test.end();
27 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 tape("stackOrderNone(series) returns [0, 1, … series.length - 1]", function(test) {
4 test.deepEqual(shape.stackOrderNone(new Array(4)), [0, 1, 2, 3]);
5 test.end();
6 });
0 var tape = require("tape"),
1 shape = require("../../");
2
3 tape("stackOrderReverse(series) returns [series.length - 1, series.length - 2, … 0]", function(test) {
4 test.deepEqual(shape.stackOrderReverse(new Array(4)), [3, 2, 1, 0]);
5 test.end();
6 });
0 var tape = require("tape");
1
2 var reNumber = /[-+]?(?:\d+\.\d+|\d+\.|\.\d+|\d+)(?:[eE][-]?\d+)?/g;
3
4 tape.Test.prototype.pathEqual = function(actual, expected) {
5 actual = normalizePath(actual + "");
6 // expected = normalizePath(expected + "");
7 this._assert(actual === expected, {
8 message: "should be equal",
9 operator: "pathEqual",
10 actual: actual,
11 expected: expected
12 });
13 };
14
15 function normalizePath(path) {
16 return path.replace(reNumber, formatNumber);
17 }
18
19 function formatNumber(s) {
20 return Math.abs((s = +s) - Math.round(s)) < 1e-6 ? Math.round(s) : s.toFixed(6);
21 }
0 var tape = require("tape"),
1 shape = require("../");
2
3 tape("pie() returns a default pie shape", function(test) {
4 var p = shape.pie();
5 test.equal(p.value()(42), 42);
6 test.ok(p.sortValues()(1, 2) > 0);
7 test.ok(p.sortValues()(2, 1) < 0);
8 test.equal(p.sortValues()(1, 1), 0);
9 test.equal(p.sort(), null);
10 test.equal(p.startAngle()(), 0);
11 test.equal(p.endAngle()(), 2 * Math.PI);
12 test.equal(p.padAngle()(), 0);
13 test.end();
14 });
15
16 tape("pie(data) returns arcs in input order", function(test) {
17 var p = shape.pie();
18 test.deepEqual(p([1, 3, 2]), [
19 {data: 1, value: 1, index: 2, startAngle: 5.235987755982988, endAngle: 6.283185307179585, padAngle: 0},
20 {data: 3, value: 3, index: 0, startAngle: 0.000000000000000, endAngle: 3.141592653589793, padAngle: 0},
21 {data: 2, value: 2, index: 1, startAngle: 3.141592653589793, endAngle: 5.235987755982988, padAngle: 0}
22 ]);
23 test.end();
24 });
25
26 tape("pie(data) coerces the specified value to a number", function(test) {
27 var p = shape.pie(), three = {valueOf: function() { return 3; }};
28 test.deepEqual(p(["1", three, "2"]), [
29 {data: "1", value: 1, index: 2, startAngle: 5.235987755982988, endAngle: 6.283185307179585, padAngle: 0},
30 {data: three, value: 3, index: 0, startAngle: 0.000000000000000, endAngle: 3.141592653589793, padAngle: 0},
31 {data: "2", value: 2, index: 1, startAngle: 3.141592653589793, endAngle: 5.235987755982988, padAngle: 0}
32 ]);
33 test.end();
34 });
35
36 tape("pie(data) treats negative values as zero", function(test) {
37 var p = shape.pie();
38 test.deepEqual(p([1, 0, -1]), [
39 {data: 1, value: 1, index: 0, startAngle: 0.000000000000000, endAngle: 6.283185307179586, padAngle: 0},
40 {data: 0, value: 0, index: 1, startAngle: 6.283185307179586, endAngle: 6.283185307179586, padAngle: 0},
41 {data: -1, value: -1, index: 2, startAngle: 6.283185307179586, endAngle: 6.283185307179586, padAngle: 0}
42 ]);
43 test.end();
44 });
45
46 tape("pie(data) treats NaN values as zero", function(test) {
47 var p = shape.pie(),
48 actual = p([1, NaN, undefined]),
49 expected = [
50 {data: 1, value: 1, index: 0, startAngle: 0.000000000000000, endAngle: 6.283185307179586, padAngle: 0},
51 {data: NaN, value: NaN, index: 1, startAngle: 6.283185307179586, endAngle: 6.283185307179586, padAngle: 0},
52 {data: undefined, value: NaN, index: 2, startAngle: 6.283185307179586, endAngle: 6.283185307179586, padAngle: 0}
53 ];
54 test.ok(isNaN(actual[1].data));
55 test.ok(isNaN(actual[1].value));
56 test.ok(isNaN(actual[2].value));
57 actual[1].data = actual[1].value = actual[2].value =
58 expected[1].data = expected[1].value = expected[2].value = {}; // deepEqual NaN
59 test.deepEqual(actual, expected);
60 test.end();
61 });
62
63 tape("pie(data) puts everything at the startAngle when the sum is zero", function(test) {
64 var p = shape.pie();
65 test.deepEqual(p([0, 0]), [
66 {data: 0, value: 0, index: 0, startAngle: 0, endAngle: 0, padAngle: 0},
67 {data: 0, value: 0, index: 1, startAngle: 0, endAngle: 0, padAngle: 0}
68 ]);
69 test.deepEqual(p.startAngle(1)([0, 0]), [
70 {data: 0, value: 0, index: 0, startAngle: 1, endAngle: 1, padAngle: 0},
71 {data: 0, value: 0, index: 1, startAngle: 1, endAngle: 1, padAngle: 0}
72 ]);
73 test.end();
74 });
75
76 tape("pie(data) restricts |endAngle - startAngle| to τ", function(test) {
77 var p = shape.pie();
78 test.deepEqual(p.startAngle(0).endAngle(7)([1, 2]), [
79 {data: 1, value: 1, index: 1, startAngle: 4.1887902047863905, endAngle: 6.2831853071795860, padAngle: 0},
80 {data: 2, value: 2, index: 0, startAngle: 0.0000000000000000, endAngle: 4.1887902047863905, padAngle: 0}
81 ]);
82 test.deepEqual(p.startAngle(7).endAngle(0)([1, 2]), [
83 {data: 1, value: 1, index: 1, startAngle: 2.8112097952136095, endAngle: 0.7168146928204142, padAngle: 0},
84 {data: 2, value: 2, index: 0, startAngle: 7.0000000000000000, endAngle: 2.8112097952136095, padAngle: 0}
85 ]);
86 test.deepEqual(p.startAngle(1).endAngle(8)([1, 2]), [
87 {data: 1, value: 1, index: 1, startAngle: 5.1887902047863905, endAngle: 7.2831853071795860, padAngle: 0},
88 {data: 2, value: 2, index: 0, startAngle: 1.0000000000000000, endAngle: 5.1887902047863905, padAngle: 0}
89 ]);
90 test.deepEqual(p.startAngle(8).endAngle(1)([1, 2]), [
91 {data: 1, value: 1, index: 1, startAngle: 3.8112097952136095, endAngle: 1.7168146928204142, padAngle: 0},
92 {data: 2, value: 2, index: 0, startAngle: 8.0000000000000000, endAngle: 3.8112097952136095, padAngle: 0}
93 ]);
94 test.end();
95 });
96
97 tape("pie.value(value)(data) observes the specified value function", function(test) {
98 test.deepEqual(shape.pie().value(function(d, i) { return i; })(new Array(3)), [
99 {data: undefined, value: 0, index: 2, startAngle: 6.2831853071795860, endAngle: 6.2831853071795860, padAngle: 0},
100 {data: undefined, value: 1, index: 1, startAngle: 4.1887902047863905, endAngle: 6.2831853071795860, padAngle: 0},
101 {data: undefined, value: 2, index: 0, startAngle: 0.0000000000000000, endAngle: 4.1887902047863905, padAngle: 0}
102 ]);
103 test.end();
104 });
105
106 tape("pie.value(f)(data) passes d, i and data to the specified function f", function(test) {
107 var data = ["a", "b"], actual = [];
108 shape.pie().value(function() { actual.push([].slice.call(arguments)); })(data);
109 test.deepEqual(actual, [["a", 0, data], ["b", 1, data]]);
110 test.end();
111 });
112
113 tape("pie().startAngle(f)(…) propagates the context and arguments to the specified function f", function(test) {
114 var expected = {that: {}, args: [42]}, actual;
115 shape.pie().startAngle(function() { actual = {that: this, args: [].slice.call(arguments)}; }).apply(expected.that, expected.args);
116 test.deepEqual(actual, expected);
117 test.end();
118 });
119
120 tape("pie().startAngle(θ)(data) observes the specified start angle", function(test) {
121 test.deepEqual(shape.pie().startAngle(Math.PI)([1, 2, 3]), [
122 {data: 1, value: 1, index: 2, startAngle: 5.759586531581287, endAngle: 6.283185307179586, padAngle: 0},
123 {data: 2, value: 2, index: 1, startAngle: 4.712388980384690, endAngle: 5.759586531581287, padAngle: 0},
124 {data: 3, value: 3, index: 0, startAngle: 3.141592653589793, endAngle: 4.712388980384690, padAngle: 0}
125 ]);
126 test.end();
127 });
128
129 tape("pie().endAngle(θ)(data) observes the specified end angle", function(test) {
130 test.deepEqual(shape.pie().endAngle(Math.PI)([1, 2, 3]), [
131 {data: 1, value: 1, index: 2, startAngle: 2.6179938779914940, endAngle: 3.1415926535897927, padAngle: 0},
132 {data: 2, value: 2, index: 1, startAngle: 1.5707963267948966, endAngle: 2.6179938779914940, padAngle: 0},
133 {data: 3, value: 3, index: 0, startAngle: 0.0000000000000000, endAngle: 1.5707963267948966, padAngle: 0}
134 ]);
135 test.end();
136 });
137
138 tape("pie().padAngle(δ)(data) observes the specified pad angle", function(test) {
139 test.deepEqual(shape.pie().padAngle(0.1)([1, 2, 3]), [
140 {data: 1, value: 1, index: 2, startAngle: 5.1859877559829880, endAngle: 6.2831853071795850, padAngle: 0.1},
141 {data: 2, value: 2, index: 1, startAngle: 3.0915926535897933, endAngle: 5.1859877559829880, padAngle: 0.1},
142 {data: 3, value: 3, index: 0, startAngle: 0.0000000000000000, endAngle: 3.0915926535897933, padAngle: 0.1}
143 ]);
144 test.end();
145 });
146
147 tape("pie().endAngle(f)(…) propagates the context and arguments to the specified function f", function(test) {
148 var expected = {that: {}, args: [42]}, actual;
149 shape.pie().endAngle(function() { actual = {that: this, args: [].slice.call(arguments)}; }).apply(expected.that, expected.args);
150 test.deepEqual(actual, expected);
151 test.end();
152 });
153
154 tape("pie().padAngle(f)(…) propagates the context and arguments to the specified function f", function(test) {
155 var expected = {that: {}, args: [42]}, actual;
156 shape.pie().padAngle(function() { actual = {that: this, args: [].slice.call(arguments)}; }).apply(expected.that, expected.args);
157 test.deepEqual(actual, expected);
158 test.end();
159 });
160
161 tape("pie().startAngle(θ₀).endAngle(θ₁).padAngle(δ)(data) restricts the pad angle to |θ₁ - θ₀| / data.length", function(test) {
162 test.deepEqual(shape.pie().startAngle(0).endAngle(Math.PI).padAngle(Infinity)([1, 2, 3]), [
163 {data: 1, value: 1, index: 2, startAngle: 2.0943951023931953, endAngle: 3.1415926535897930, padAngle: 1.0471975511965976},
164 {data: 2, value: 2, index: 1, startAngle: 1.0471975511965976, endAngle: 2.0943951023931953, padAngle: 1.0471975511965976},
165 {data: 3, value: 3, index: 0, startAngle: 0.0000000000000000, endAngle: 1.0471975511965976, padAngle: 1.0471975511965976}
166 ]);
167 test.deepEqual(shape.pie().startAngle(0).endAngle(-Math.PI).padAngle(Infinity)([1, 2, 3]), [
168 {data: 1, value: 1, index: 2, startAngle: -2.0943951023931953, endAngle: -3.1415926535897930, padAngle: 1.0471975511965976},
169 {data: 2, value: 2, index: 1, startAngle: -1.0471975511965976, endAngle: -2.0943951023931953, padAngle: 1.0471975511965976},
170 {data: 3, value: 3, index: 0, startAngle: -0.0000000000000000, endAngle: -1.0471975511965976, padAngle: 1.0471975511965976}
171 ]);
172 test.end();
173 });
174
175 tape("pie.sortValues(f) sorts arcs by value per the specified comparator function f", function(test) {
176 var p = shape.pie();
177 test.deepEqual(p.sortValues(function(a, b) { return a - b; })([1, 3, 2]), [
178 {data: 1, value: 1, index: 0, startAngle: 0.0000000000000000, endAngle: 1.0471975511965976, padAngle: 0},
179 {data: 3, value: 3, index: 2, startAngle: 3.1415926535897930, endAngle: 6.2831853071795860, padAngle: 0},
180 {data: 2, value: 2, index: 1, startAngle: 1.0471975511965976, endAngle: 3.1415926535897930, padAngle: 0}
181 ]);
182 test.deepEqual(p.sortValues(function(a, b) { return b - a; })([1, 3, 2]), [
183 {data: 1, value: 1, index: 2, startAngle: 5.2359877559829880, endAngle: 6.2831853071795850, padAngle: 0},
184 {data: 3, value: 3, index: 0, startAngle: 0.0000000000000000, endAngle: 3.1415926535897930, padAngle: 0},
185 {data: 2, value: 2, index: 1, startAngle: 3.1415926535897930, endAngle: 5.2359877559829880, padAngle: 0}
186 ]);
187 test.equal(p.sort(), null);
188 test.end();
189 });
190
191 tape("pie.sort(f) sorts arcs by data per the specified comparator function f", function(test) {
192 var a = {valueOf: function() { return 1; }, name: "a"},
193 b = {valueOf: function() { return 2; }, name: "b"},
194 c = {valueOf: function() { return 3; }, name: "c"},
195 p = shape.pie();
196 test.deepEqual(p.sort(function(a, b) { return a.name.localeCompare(b.name); })([a, c, b]), [
197 {data: a, value: 1, index: 0, startAngle: 0.0000000000000000, endAngle: 1.0471975511965976, padAngle: 0},
198 {data: c, value: 3, index: 2, startAngle: 3.1415926535897930, endAngle: 6.2831853071795860, padAngle: 0},
199 {data: b, value: 2, index: 1, startAngle: 1.0471975511965976, endAngle: 3.1415926535897930, padAngle: 0}
200 ]);
201 test.deepEqual(p.sort(function(a, b) { return b.name.localeCompare(a.name); })([a, c, b]), [
202 {data: a, value: 1, index: 2, startAngle: 5.2359877559829880, endAngle: 6.2831853071795850, padAngle: 0},
203 {data: c, value: 3, index: 0, startAngle: 0.0000000000000000, endAngle: 3.1415926535897930, padAngle: 0},
204 {data: b, value: 2, index: 1, startAngle: 3.1415926535897930, endAngle: 5.2359877559829880, padAngle: 0}
205 ]);
206 test.equal(p.sortValues(), null);
207 test.end();
208 });
0 var polygon = require("d3-polygon");
1
2 module.exports = function() {
3 return {
4 points: null,
5 area: function() { return Math.abs(polygon.polygonArea(this.points)); },
6 moveTo: function(x, y) { this.points = [[x, y]]; },
7 lineTo: function(x, y) { this.points.push([x, y]); },
8 rect: function(x, y, w, h) { this.points = [[x, y], [x + w, y], [x + w, y + h], [x, y + h]]; },
9 closePath: function() {}
10 };
11 };
0 var tape = require("tape"),
1 shape = require("../");
2
3 tape("stack() has the expected defaults", function(test) {
4 var s = shape.stack();
5 test.deepEqual(s.keys()(), []);
6 test.equal(s.value()({foo: 42}, "foo"), 42);
7 test.equal(s.order(), shape.stackOrderNone);
8 test.equal(s.offset(), shape.stackOffsetNone);
9 test.end();
10 });
11
12 tape("stack(data) computes the stacked series for the given data", function(test) {
13 var s = shape.stack().keys([0, 1, 2, 3]),
14 data = [[1, 3, 5, 1], [2, 4, 2, 3], [1, 2, 4, 2]];
15 test.deepEqual(s(data), [
16 series([[0, 1], [0, 2], [0, 1]], data, 0, 0),
17 series([[1, 4], [2, 6], [1, 3]], data, 1, 1),
18 series([[4, 9], [6, 8], [3, 7]], data, 2, 2),
19 series([[9, 10], [8, 11], [7, 9]], data, 3, 3)
20 ]);
21 test.end();
22 });
23
24 tape("stack.keys(array) sets the array of constant keys", function(test) {
25 var s = shape.stack().keys(["0.0", "2.0", "4.0"]);
26 test.deepEqual(s.keys()(), ["0.0", "2.0", "4.0"]);
27 test.end();
28 });
29
30 tape("stack.keys(function) sets the key accessor function", function(test) {
31 var s = shape.stack().keys(function() { return "abc".split(""); });
32 test.deepEqual(s.keys()(), ["a", "b", "c"]);
33 test.end();
34 });
35
36 tape("stack(data, arguments…) passes the key accessor any additional arguments", function(test) {
37 var A,
38 B,
39 k = function(data, a, b) { A = a, B = b; return Object.keys(data[0]); },
40 s = shape.stack().keys(k),
41 data = [[1, 3, 5, 1], [2, 4, 2, 3], [1, 2, 4, 2]];
42 test.deepEqual(s(data, "foo", "bar"), [
43 series([[0, 1], [0, 2], [0, 1]], data, "0", 0),
44 series([[1, 4], [2, 6], [1, 3]], data, "1", 1),
45 series([[4, 9], [6, 8], [3, 7]], data, "2", 2),
46 series([[9, 10], [8, 11], [7, 9]], data, "3", 3)
47 ]);
48 test.equal(A, "foo");
49 test.equal(B, "bar");
50 test.end();
51 });
52
53 tape("stack.value(number) sets the constant value", function(test) {
54 var s = shape.stack().value("42.0");
55 test.equal(s.value()(), 42);
56 test.end();
57 });
58
59 tape("stack.value(function) sets the value accessor function", function(test) {
60 var v = function() { return 42; },
61 s = shape.stack().value(v);
62 test.equal(s.value(), v);
63 test.end();
64 });
65
66 tape("stack(data) passes the value accessor datum, key, index and data", function(test) {
67 var actual,
68 v = function(d, k, i, data) { actual = {datum: d, key: k, index: i, data: data}; return 2; },
69 s = shape.stack().keys(["foo"]).value(v),
70 data = [{foo: 1}];
71 test.deepEqual(s(data), [series([[0, 2]], data, "foo", 0)]);
72 test.deepEqual(actual, {datum: data[0], key: "foo", index: 0, data: data});
73 test.end();
74 });
75
76 tape("stack(data) coerces the return value of the value accessor to a number", function(test) {
77 var actual,
78 v = function() { return "2.0"; },
79 s = shape.stack().keys(["foo"]).value(v),
80 data = [{foo: 1}];
81 test.deepEqual(s(data), [series([[0, 2]], data, "foo", 0)]);
82 test.end();
83 });
84
85 tape("stack.order(null) is equivalent to stack.order(stackOrderNone)", function(test) {
86 var s = shape.stack().order(null);
87 test.equal(s.order(), shape.stackOrderNone);
88 test.equal(typeof s.order(), "function");
89 test.end();
90 });
91
92 tape("stack.order(function) sets the order function", function(test) {
93 var s = shape.stack().keys([0, 1, 2, 3]).order(shape.stackOrderReverse),
94 data = [[1, 3, 5, 1], [2, 4, 2, 3], [1, 2, 4, 2]];
95 test.equal(s.order(), shape.stackOrderReverse);
96 test.deepEqual(s(data), [
97 series([[9, 10], [9, 11], [8, 9]], data, 0, 3),
98 series([[6, 9], [5, 9], [6, 8]], data, 1, 2),
99 series([[1, 6], [3, 5], [2, 6]], data, 2, 1),
100 series([[0, 1], [0, 3], [0, 2]], data, 3, 0)
101 ]);
102 test.end();
103 });
104
105 tape("stack.offset(null) is equivalent to stack.offset(stackOffsetNone)", function(test) {
106 var s = shape.stack().offset(null);
107 test.equal(s.offset(), shape.stackOffsetNone);
108 test.equal(typeof s.offset(), "function");
109 test.end();
110 });
111
112 tape("stack.offset(function) sets the offset function", function(test) {
113 var s = shape.stack().keys([0, 1, 2, 3]).offset(shape.stackOffsetExpand),
114 data = [[1, 3, 5, 1], [2, 4, 2, 3], [1, 2, 4, 2]];
115 test.equal(s.offset(), shape.stackOffsetExpand);
116 test.deepEqual(s(data).map(roundSeries), [
117 [[0 / 10, 1 / 10], [0 / 11, 2 / 11], [0 / 9, 1 / 9]],
118 [[1 / 10, 4 / 10], [2 / 11, 6 / 11], [1 / 9, 3 / 9]],
119 [[4 / 10, 9 / 10], [6 / 11, 8 / 11], [3 / 9, 7 / 9]],
120 [[9 / 10, 10 / 10], [8 / 11, 11 / 11], [7 / 9, 9 / 9]]
121 ].map(roundSeries));
122 test.end();
123 });
124
125 function series(series, data, key, index) {
126 data.forEach(function(d, i) { series[i].data = d; });
127 series.key = key;
128 series.index = index;
129 return series;
130 }
131
132 function roundSeries(series) {
133 return series.map(function(point) {
134 return point.map(function(value) {
135 return Math.round(value * 1e6) / 1e6;
136 });
137 });
138 }
0 var tape = require("tape"),
1 shape = require("../"),
2 polygonContext = require("./polygonContext");
3
4 require("./inDelta");
5 require("./pathEqual");
6
7 tape("symbol() returns a default symbol shape", function(test) {
8 var s = shape.symbol();
9 test.equal(s.type()(), shape.symbolCircle);
10 test.equal(s.size()(), 64);
11 test.equal(s.context(), null);
12 test.pathEqual(s(), "M4.513517,0A4.513517,4.513517,0,1,1,-4.513517,0A4.513517,4.513517,0,1,1,4.513517,0");
13 test.end();
14 });
15
16 tape("symbol().size(f)(…) propagates the context and arguments to the specified function", function(test) {
17 var expected = {that: {}, args: [42]}, actual;
18 shape.symbol().size(function() { actual = {that: this, args: [].slice.call(arguments)}; return 64; }).apply(expected.that, expected.args);
19 test.deepEqual(actual, expected);
20 test.end();
21 });
22
23 tape("symbol().type(f)(…) propagates the context and arguments to the specified function", function(test) {
24 var expected = {that: {}, args: [42]}, actual;
25 shape.symbol().type(function() { actual = {that: this, args: [].slice.call(arguments)}; return shape.symbolCircle; }).apply(expected.that, expected.args);
26 test.deepEqual(actual, expected);
27 test.end();
28 });
29
30 tape("symbol.size(size) observes the specified size function", function(test) {
31 var size = function(d, i) { return d.z * 2 + i; },
32 s = shape.symbol().size(size);
33 test.equal(s.size(), size);
34 test.pathEqual(s({z: 0}, 0), "M0,0");
35 test.pathEqual(s({z: Math.PI / 2}, 0), "M1,0A1,1,0,1,1,-1,0A1,1,0,1,1,1,0");
36 test.pathEqual(s({z: 2 * Math.PI}, 0), "M2,0A2,2,0,1,1,-2,0A2,2,0,1,1,2,0");
37 test.pathEqual(s({z: Math.PI}, 1), "M1.522600,0A1.522600,1.522600,0,1,1,-1.522600,0A1.522600,1.522600,0,1,1,1.522600,0");
38 test.pathEqual(s({z: 4 * Math.PI}, 2), "M2.938813,0A2.938813,2.938813,0,1,1,-2.938813,0A2.938813,2.938813,0,1,1,2.938813,0");
39 test.end();
40 });
41
42 tape("symbol.size(size) observes the specified size constant", function(test) {
43 var s = shape.symbol();
44 test.equal(s.size(42).size()(), 42);
45 test.pathEqual(s.size(0)(), "M0,0");
46 test.pathEqual(s.size(Math.PI)(), "M1,0A1,1,0,1,1,-1,0A1,1,0,1,1,1,0");
47 test.pathEqual(s.size(4 * Math.PI)(), "M2,0A2,2,0,1,1,-2,0A2,2,0,1,1,2,0");
48 test.end();
49 });
50
51 tape("symbol.type(symbolCircle) generates the expected path", function(test) {
52 var s = shape.symbol().type(shape.symbolCircle).size(function(d) { return d; });
53 test.pathEqual(s(0), "M0,0");
54 test.pathEqual(s(20), "M2.523133,0A2.523133,2.523133,0,1,1,-2.523133,0A2.523133,2.523133,0,1,1,2.523133,0");
55 test.end();
56 });
57
58 tape("symbol.type(symbolCross) generates a polygon with the specified size", function(test) {
59 var p = polygonContext(), s = shape.symbol().type(shape.symbolCross).context(p);
60 s.size(1)(); test.inDelta(p.area(), 1);
61 s.size(240)(); test.inDelta(p.area(), 240);
62 test.end();
63 });
64
65 tape("symbol.type(symbolCross) generates the expected path", function(test) {
66 var s = shape.symbol().type(shape.symbolCross).size(function(d) { return d; });
67 test.pathEqual(s(0), "M0,0L0,0L0,0L0,0L0,0L0,0L0,0L0,0L0,0L0,0L0,0L0,0Z");
68 test.pathEqual(s(20), "M-3,-1L-1,-1L-1,-3L1,-3L1,-1L3,-1L3,1L1,1L1,3L-1,3L-1,1L-3,1Z");
69 test.end();
70 });
71
72 tape("symbol.type(symbolDiamond) generates a polygon with the specified size", function(test) {
73 var p = polygonContext(), s = shape.symbol().type(shape.symbolDiamond).context(p);
74 s.size(1)(); test.inDelta(p.area(), 1);
75 s.size(240)(); test.inDelta(p.area(), 240);
76 test.end();
77 });
78
79 tape("symbol.type(symbolDiamond) generates the expected path", function(test) {
80 var s = shape.symbol().type(shape.symbolDiamond).size(function(d) { return d; });
81 test.pathEqual(s(0), "M0,0L0,0L0,0L0,0Z");
82 test.pathEqual(s(10), "M0,-2.942831L1.699044,0L0,2.942831L-1.699044,0Z");
83 test.end();
84 });
85
86 tape("symbol.type(symbolStar) generates a polygon with the specified size", function(test) {
87 var p = polygonContext(), s = shape.symbol().type(shape.symbolStar).context(p);
88 s.size(1)(); test.inDelta(p.area(), 1);
89 s.size(240)(); test.inDelta(p.area(), 240);
90 test.end();
91 });
92
93 tape("symbol.type(symbolStar) generates the expected path", function(test) {
94 var s = shape.symbol().type(shape.symbolStar).size(function(d) { return d; });
95 test.pathEqual(s(0), "M0,0L0,0L0,0L0,0L0,0L0,0L0,0L0,0L0,0L0,0Z");
96 test.pathEqual(s(10), "M0,-2.984649L0.670095,-0.922307L2.838570,-0.922307L1.084237,0.352290L1.754333,2.414632L0,1.140035L-1.754333,2.414632L-1.084237,0.352290L-2.838570,-0.922307L-0.670095,-0.922307Z");
97 test.end();
98 });
99
100 tape("symbol.type(symbolSquare) generates a polygon with the specified size", function(test) {
101 var p = polygonContext(), s = shape.symbol().type(shape.symbolSquare).context(p);
102 s.size(1)(); test.inDelta(p.area(), 1);
103 s.size(240)(); test.inDelta(p.area(), 240);
104 test.end();
105 });
106
107 tape("symbol.type(symbolSquare) generates the expected path", function(test) {
108 var s = shape.symbol().type(shape.symbolSquare).size(function(d) { return d; });
109 test.pathEqual(s(0), "M0,0h0v0h0Z");
110 test.pathEqual(s(4), "M-1,-1h2v2h-2Z");
111 test.pathEqual(s(16), "M-2,-2h4v4h-4Z");
112 test.end();
113 });
114
115 tape("symbol.type(symbolTriangle) generates a polygon with the specified size", function(test) {
116 var p = polygonContext(), s = shape.symbol().type(shape.symbolTriangle).context(p);
117 s.size(1)(); test.inDelta(p.area(), 1);
118 s.size(240)(); test.inDelta(p.area(), 240);
119 test.end();
120 });
121
122 tape("symbol.type(symbolTriangle) generates the expected path", function(test) {
123 var s = shape.symbol().type(shape.symbolTriangle).size(function(d) { return d; });
124 test.pathEqual(s(0), "M0,0L0,0L0,0Z");
125 test.pathEqual(s(10), "M0,-2.774528L2.402811,1.387264L-2.402811,1.387264Z");
126 test.end();
127 });
128
129 tape("symbol.type(symbolWye) generates a polygon with the specified size", function(test) {
130 var p = polygonContext(), s = shape.symbol().type(shape.symbolWye).context(p);
131 s.size(1)(); test.inDelta(p.area(), 1);
132 s.size(240)(); test.inDelta(p.area(), 240);
133 test.end();
134 });
135
136 tape("symbol.type(symbolWye) generates the expected path", function(test) {
137 var s = shape.symbol().type(shape.symbolWye).size(function(d) { return d; });
138 test.pathEqual(s(0), "M0,0L0,0L0,0L0,0L0,0L0,0L0,0L0,0L0,0Z");
139 test.pathEqual(s(10), "M0.853360,0.492688L0.853360,2.199408L-0.853360,2.199408L-0.853360,0.492688L-2.331423,-0.360672L-1.478063,-1.838735L0,-0.985375L1.478063,-1.838735L2.331423,-0.360672Z");
140 test.end();
141 });
0 var tape = require("tape"),
1 shape = require("../");
2
3 tape("symbols is the array of symbol types", function(test) {
4 test.deepEqual(shape.symbols, [
5 shape.symbolCircle,
6 shape.symbolCross,
7 shape.symbolDiamond,
8 shape.symbolSquare,
9 shape.symbolStar,
10 shape.symbolTriangle,
11 shape.symbolWye
12 ]);
13 test.end();
14 });