├── .babelrc
├── .eslintrc
├── .gitignore
├── .npmignore
├── CONTRIBUTING.md
├── README.md
├── build
├── d3-loom.js
├── d3-loom.js.map
└── d3-loom.min.js
├── example
├── index.html
└── lotr_words_location.json
├── index.js
├── lotr.png
├── package-lock.json
├── package.json
├── rollup.config.js
└── src
├── compare-value.js
├── constant.js
├── loom.js
└── string.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [ "es2015" ]
3 | }
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb-base",
3 | "rules": {
4 | "no-param-reassign": "off",
5 | "no-use-before-define": "off",
6 | "func-names": "off",
7 | "no-return-assign": "off",
8 | "no-mixed-operators": "off",
9 | "comma-dangle": "off",
10 | "space-before-function-paren": "off"
11 | }
12 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | npm-debug.log
4 | *.tgz
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | build/*.zip
2 | test/
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute
2 |
3 | Any feedback, questions, bug reports, and general discussions are most welcome.
4 |
5 | Please [submit an issue](https://github.com/nbremer/d3-loom/issues/new) to start a discussion.
6 |
7 | If you've created something using d3-loom, we'd love to hear about it! Please [submit an issue](https://github.com/nbremer/d3-loom/issues/new) to request adding a link in the README to your example.
8 |
9 | To get started with development,
10 |
11 | * clone the repository,
12 | * run `npm install` to install dependencies
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # d3-loom
2 |
3 | This is a d3 plugin to create a "loom" visualization. For an extensive explanation of the effects of all the settings decribed farther below, please see the [blog/tutorial I wrote about the loom here](https://www.visualcinnamon.com/2017/08/d3-loom.html).
4 |
5 | [](https://bl.ocks.org/nbremer/4530f11952a3ef7e007ad6ef93d5adb3)
6 |
7 | The loom layout is meant to create a chart with a group of entities in the center and different group of entities on the outside. They are connected by *strings* where the thickness of the string on the outside represents the connection (i.e. value) of the inner and outer entity.
8 |
9 | Or in the words of Robert Kosara
10 | > One chart to rule them all, one chart to find them; one chart to bring them all and in the darkness bind them
11 |
12 | For example, in the above image, the inner entities are the characters of the Fellowship in the Lord of the Rings movies. The outer entities are the locations in Middle Earth where the movie takes place. The connection/value is the number of words spoken by each character at each location.
13 |
14 | Since this layout was transformed from d3's [chord diagram](https://github.com/d3/d3-chord/blob/master/README.md) many of the below API references will be similar to those from the chord diagram for the *loom* and similar to the ribbon functions for the *strings*.
15 |
16 | ## Installing
17 |
18 | Download the [latest build](build/). **d3-loom** depends on **d3**, so be sure to include a script tag with the [d3 library](http://d3js.org/) before including `d3-loom.js`. In a vanilla environment, a d3 global is exported:
19 |
20 |
21 | ```html
22 |
23 |
24 |
25 |
30 | ```
31 |
32 | If you use a package manager like [npm](https://www.npmjs.com/) or [yarn](https://yarnpkg.com/en/), say
33 |
34 | ```
35 | npm install d3-loom
36 | ```
37 |
38 | or
39 |
40 | ```
41 | yarn add d3-loom
42 | ```
43 |
44 | to add [d3-loom](https://www.npmjs.com/package/d3-loom) to your project. AMD, CommonJS, and vanilla environments are supported.
45 |
46 | ## Note
47 |
48 | One thing that I have not (yet) figured out is how to sort the outer groups/entities in such a way to automatically make a visually appealing split in 2 separate halves. This is only relevant when you specify an [empty percentage](#loom_emptyPerc), thus create a gap in the top and bottom. For now you will have to manually order the outer entities in such a way that when split into two groups, the total value of those two groups separately lies close to 50%. However, you don't need to have the exact number of entities on the left half as on the right. The program will try and find a split that separates all the entities in two groups to make them both as close to 50% as possible, but it will not reorder the outer entities to do so.
49 |
50 | ## Feedback
51 |
52 | This is my first attempt at a plugin and it has definitely not been tested well enough. I would therefore love to hear from you about any bugs or errors that you run into while trying to use the plugin. You can create an Issue right here, reach me on Twitter via [@NadiehBremer](https://www.twitter.com/NadiehBremer) or mail me on info *at* visualcinnamon.com
53 |
54 |
55 | ## API Reference
56 |
57 | # d3.loom()
58 |
59 | Constructs a new loom generator with the default settings.
60 |
61 | #loom(data)
62 |
63 | Computes the loom layout for the specified *data*. The length of the returned array is the same as *data*, however, due to sorting of the strings, to reduce overlap, the ordering can be different than the initial data.
64 |
65 | Typically a dataset for the *loom* contains 3 values; the outer entity, the inner entity and the value that connects these two (e.g. outer = location, inner = person, value = days that person stayed in the location):
66 |
67 | ```js
68 | var data = [
69 | {
70 | outer: "Amsterdam",
71 | inner: "Alexander",
72 | value: 679
73 | },
74 | {
75 | outer: "Bangkok",
76 | inner: "Brendan",
77 | value: 124
78 | },
79 | //...and so on
80 | ];
81 | ```
82 |
83 | The return value of *loom*(*data*) is an array of *looms*, where each loom represents the connection between an entity in the center of the loom (the *inner*) and the entity on the outside of the loom (the *outer*) and is an object with the following properties:
84 |
85 | * `inner` - the inner subgroup
86 | * `outer` - the outer subgroup
87 |
88 | Both the inner and outer subgroup are also objects. The inner has the following properties:
89 |
90 | * `name` - the [name](#loom_inner) of the inner entity
91 | * `offset` - the [horizontal offset](#loom_widthInner) of the inner string's endpoint from the center
92 | * `x` - the horizontal location of the inner entity
93 | * `y` - the vertical location of the inner entity
94 |
95 | And the outer has the following properties:
96 |
97 | * `groupStartAngle` - the [start angle](#string_groupStartAngle) of the outer group to which the specific string belongs
98 | * `startAngle` - the [start angle](#string_startAngle) of the string at the outer edge in radians
99 | * `endAngle` - the [end angle](#string_endAngle) of the string at the outer edge in radians
100 | * `value` - the numeric [value](#loom_value) of the string
101 | * `index` - the zero-based [sorted index](#loom_sortGroups) of the group
102 | * `subindex` - the zero-based [sorted sub-index](#loom_sortSubgroups) of the string within the group
103 | * `innername` - the [name](#loom_inner) of the connected inner entity
104 | * `outername` - the [name](#loom_outer) of the outer entity
105 |
106 | The *looms* are passed to [d3.string](#string) to display the relationships between the inner and outer entities.
107 |
108 | The *looms* array also defines a two secondary arrays. The first, called *groups*, is an array representing the outer entities. The length of the array is the number of unique outer entities and is an object with the following properties:
109 |
110 | * `startAngle` - the [start angle](#string_startAngle) of the arc in radians
111 | * `endAngle` - the [end angle](#string_endAngle) of the arc in radians
112 | * `value` - the numeric [value](#loom_value) of the arc
113 | * `index` - the zero-based [sorted index](#loom_sortGroups) of the arc
114 | * `outername` - the [name](#loom_outer) of the outer entity
115 |
116 | The *groups* are passed to [d3.arc](https://github.com/d3/d3-shape#arc) to produce a donut chart around the circumference of the loom layout.
117 |
118 | The other array, called, *innergroups*, is an array represting the inner entities. The length of the array is the number of unique inner entities and is an object with the following properties:
119 |
120 | * `name` - the [name](#loom_inner) of the inner entity
121 | * `offset` - the [horizontal offset](#loom_widthInner) of the inner string's endpoint from the center
122 | * `x` - the horizontal location of the inner entity
123 | * `y` - the vertical location of the inner entity
124 |
125 | The *innergroups* are used to create the textual representation of the inner entities in the center of the loom layout.
126 |
127 | #loom.padAngle([angle])
128 |
129 | If *angle* is specified, sets the pad angle (i.e. the white space) between adjacent outer groups to the specified number in radians and returns this loom layout. If *angle* is not specified, returns the current pad angle, which defaults to zero.
130 |
131 | #loom.inner([inner])
132 |
133 | The *inner* represents the name/id/... textual value of the entities that will be placed in the center. If *inner* is specified, sets the inner accessor to the specified function and returns this string generator. If *inner* is not specified, returns the current inner accessor, which defaults to:
134 |
135 | ```js
136 | function inner(d) {
137 | return d.inner;
138 | }
139 | ```
140 |
141 | #loom.outer([outer])
142 |
143 | The *outer* represents the name/id/... textual value of the entities that will be placed around the loom along a circle. If *outer* is specified, sets the outer accessor to the specified function and returns this string generator. If *outer* is not specified, returns the current outer accessor, which defaults to:
144 |
145 | ```js
146 | function outer(d) {
147 | return d.outer;
148 | }
149 | ```
150 |
151 | #loom.value([value])
152 |
153 | The *value* represents the numeric value that is the connection between the inner and outer entity. It is the value that determines the width of the strings on the outside. If *value* is specified, sets the value accessor to the specified function and returns this string generator. If *value* is not specified, returns the current value accessor, which defaults to:
154 |
155 | ```js
156 | function value(d) {
157 | return d.value;
158 | }
159 | ```
160 |
161 | #loom.heightInner([height])
162 |
163 | This *height* gives the vertical distance between the inner entities in the center. If *height* is specified, sets the heightInner to the specified number and returns this loom generator. If height is not specified, returns the current heightInner value, which defaults to 20 pixels.
164 |
165 | #loom.widthInner([width])
166 |
167 | This *width* gives the horizontal distance between the inner endpoints of the strings in the center. It is the value that determines the width of the gap that is created so the text of the inner entities does not overlap the strings. If *width* is specified, sets the widthInner to the specified function or number and returns this loom generator. However, note that this function receives a *d* value that contains the string of the entity in the center (the *inner*). You can therefore make the width depend on the length of the *inner*'s string. If width is not specified, returns the current widthInner value, which defaults to 30 pixels.
168 |
169 | #loom.emptyPerc([value])
170 |
171 | This *value* gives the percentage of the circle that will be empty to create space in the top and bottom. If *value* is specified, sets the current emptyPerc to the specified number in the range [0,1] and returns this loom generator. If value is not specified, returns the current emptyPerc value, which defaults to 0.2.
172 |
173 | #loom.sortGroups([compare])
174 |
175 | If *compare* is specified, sets the group comparator to the specified function or null and returns this loom layout. If *compare* is not specified, returns the current group comparator, which defaults to null. If the group comparator is non-null, it is used to sort the outer groups/entities by their total value (i.e. the sum of all the inner strings). See also [d3.ascending](https://github.com/d3/d3-array#ascending) and [d3.descending](https://github.com/d3/d3-array#descending).
176 |
177 | #loom.sortSubgroups([compare])
178 |
179 | If *compare* is specified, sets the subgroup comparator to the specified function or null and returns this loom layout. If *compare* is not specified, returns the current subgroup comparator, which defaults to null. If the subgroup comparator is non-null, it is used to sort the subgroups (i.e. the separate strings) within each outer entity by their value. See also [d3.ascending](https://github.com/d3/d3-array#ascending) and [d3.descending](https://github.com/d3/d3-array#descending). This sorting applies to both the order of the strings on the outer edge and the vertical order of the inner entities. It is advised to supply a subGroup sorting whenever there is not already a sorting applied to the underlying data, otherwise the inner entities and the strings will be drawn in the exact order as they appear in the data, typically resulting in a lot of overlapping strings.
180 |
181 | #loom.sortLooms([compare])
182 |
183 | If *compare* is specified, sets the loom comparator to the specified function or null and returns this loom layout. If *compare* is not specified, returns the current loom comparator, which defaults to null. If the loom comparator is non-null, it is used to sort the strings by their value; this only affects the *z*-order of these inner strings (i.e. how they overlap). See also [d3.ascending](https://github.com/d3/d3-array#ascending) and [d3.descending].(https://github.com/d3/d3-array#descending).
184 |
185 | # d3.string()
186 |
187 | Creates a new string generator with the default settings.
188 |
189 | #string(arguments…)
190 |
191 | Generates a string for the given *arguments*. The *arguments* are arbitrary; they are simply propagated to the string's generator's accessor functions along with the `this` object. If the string generator has a context, then the string is rendered to this context as a sequence of path method calls and this function returns void. Otherwise, a path data string is returned.
192 |
193 | Typically, only the [radius](#string_radius), [thicknessInner](#string_thicknessInner), and [pullout](#string_pullout) should be adjusted when used on conjunction with the [loom](#loom), because all the other accessors will work with the default settings.
194 |
195 | #string.radius([radius])
196 |
197 | If *radius* is specified, sets the radius accessor to the specified function and returns this string generator. If *radius* is not specified, returns the current radius value, which defaults to 100 pixels.
198 |
199 | The *radius* represents the inner radius of the loom and is typically set to a single number. It is advised to always set this value different from the default, depending on the space available within your svg.
200 |
201 | #string.thicknessInner([thickness])
202 |
203 | If *thickness* is specified, sets the thicknessInner to the specified number and returns this string generator. If *thickness* is not specified, returns the current thicknessInner value, which defaults to 0 pixels. The thicknessInner defines the "height", or thickness, that the strings will have at their endpoints next to the inner entities.
204 |
205 | #string.pullout([pullout])
206 |
207 | If *pullout* is specified, sets the pullout to the specified number and returns this string generator. If *pullout* is not specified, returns the current pullout value, which defaults to 50 pixels. The pullout defines how far the two circle halves will be placed outward horizontally.
208 |
209 | #string.inner([inner])
210 |
211 | If *inner* is specified, sets the inner accessor to the specified function and returns this string generator. If *inner* is not specified, returns the current source accessor, which defaults to:
212 |
213 | ```js
214 | function inner(d) {
215 | return d.inner;
216 | }
217 | ```
218 |
219 | When the string generator is used in conjunction with the *loom*, the resulting loom array will contain an *inner* object. Thus this accessor function does not have to be set separately, but just use the default.
220 |
221 | #string.outer([outer])
222 |
223 | If *outer* is specified, sets the outer accessor to the specified function and returns this string generator. If *outer* is not specified, returns the current outer accessor, which defaults to:
224 |
225 | ```js
226 | function outer(d) {
227 | return d.outer;
228 | }
229 | ```
230 |
231 | When the string generator is used in conjunction with the *loom*, the resulting loom array will contain an *outer* object. Thus this accessor function does not have to be set separately, but just use the default.
232 |
233 | #string.groupStartAngle([angle])
234 |
235 | If *angle* is specified, sets the start angle accessor to the specified function and returns this string generator. If *angle* is not specified, returns the current start angle accessor, which defaults to:
236 |
237 | ```js
238 | function groupStartAngle(d) {
239 | return d.groupStartAngle;
240 | }
241 | ```
242 |
243 | The *angle* is specified in radians, with 0 at -*y* (12 o'clock) and positive angles proceeding clockwise. This separate assessor is needed to make sure that even when an *emptyPerc* is set, all the strings belonging to the same outer group will be drawn at the same side. It's best make this assessor similar in "function" to the *startAngle* below (i.e. if a constant is added onto the *startAngle* to rotate the whole, then the same constant should be added to the *groupStartAngle*). When the string generator is used in conjunction with the *loom*, the resulting loom array will contain an *groupStartAngle* value within the *outer* object. In that case this accessor function does not have to be set separately, but just use the default.
244 |
245 | #string.startAngle([angle])
246 |
247 | If *angle* is specified, sets the start angle accessor to the specified function and returns this string generator. If *angle* is not specified, returns the current start angle accessor, which defaults to:
248 |
249 | ```js
250 | function startAngle(d) {
251 | return d.startAngle;
252 | }
253 | ```
254 |
255 | The *angle* is specified in radians, with 0 at -*y* (12 o'clock) and positive angles proceeding clockwise. When the string generator is used in conjunction with the *loom*, the resulting loom array will contain an *startAngle* value within the *outer* object. In that case this accessor function does not have to be set separately, but just use the default.
256 |
257 | #string.endAngle([angle])
258 |
259 | If *angle* is specified, sets the end angle accessor to the specified function and returns this string generator. If *angle* is not specified, returns the current end angle accessor, which defaults to:
260 |
261 | ```js
262 | function endAngle(d) {
263 | return d.endAngle;
264 | }
265 | ```
266 |
267 | The *angle* is specified in radians, with 0 at -*y* (12 o'clock) and positive angles proceeding clockwise. When the string generator is used in conjunction with the *loom*, the resulting loom array will contain an *endAngle* value within the *outer* object. In that case this accessor function does not have to be set separately, but just use the default.
268 |
269 | #string.x([x])
270 |
271 | If *x* is specified, sets the x accessor to the specified function and returns this string generator. If *x* is not specified, returns the current x accessor, which defaults to:
272 |
273 | ```js
274 | function x(d) {
275 | return d.x;
276 | }
277 | ```
278 |
279 | The *x* defines the horizontal location where the inner entities are placed, typically in the center of the loom. When the string generator is used in conjunction with the *loom*, the resulting loom array will contain an *x* value within the *inner* object. In that case this accessor function does not have to be set separately, but just use the default.
280 |
281 | #string.y([y])
282 |
283 | If *y* is specified, sets the y accessor to the specified function and returns this string generator. If *y* is not specified, returns the current y accessor, which defaults to:
284 |
285 | ```js
286 | function y(d) {
287 | return d.y;
288 | }
289 | ```
290 |
291 | The *y* defines the vertical location where the inner entities are placed. They are typically placed in a column like fashion in the center, one above the other. When the string generator is used in conjunction with the *loom*, the resulting loom array will contain an *y* value within the *inner* object. In that case this accessor function does not have to be set separately, but just use the default.
292 |
293 | #string.offset([offset])
294 |
295 | If *offset* is specified, sets the offset accessor to the specified function and returns this string generator. If *offset* is not specified, returns the current offset accessor, which defaults to:
296 |
297 | ```js
298 | function offset(d) {
299 | return d.offset;
300 | }
301 | ```
302 |
303 | The *offset* defines the horizontal space between the inner end points of the strings, so that the text of the inner entities does not overlap the strings. It is typically set through the [widthInner](#loom_widthInner) accessor of the loom and propagates through to the string function. Therefore, when the string generator is used in conjunction with the *loom*, the resulting loom array will contain an *offset* value within the *inner* object. In that case this accessor function does not have to be set separately, but just use the default.
304 |
305 | #string.context([context])
306 |
307 | If *context* is specified, sets the context and returns this string generator. If *context* is not specified, returns the current context, which defaults to null. If the context is not null, then the [generated string](#_string) 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 string is returned. See also [d3-path](https://github.com/d3/d3-path).
308 |
--------------------------------------------------------------------------------
/build/d3-loom.js:
--------------------------------------------------------------------------------
1 | (function (global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3 | typeof define === 'function' && define.amd ? define(['exports'], factory) :
4 | (factory((global.d3 = global.d3 || {})));
5 | }(this, (function (exports) { 'use strict';
6 |
7 | function compareValue(compare) {
8 | return function (a, b) {
9 | return compare(a.outer.value, b.outer.value);
10 | };
11 | }
12 |
13 | function constant(x) {
14 | return function () {
15 | return x;
16 | };
17 | }
18 |
19 | /* Based on the d3v4 d3.chord() function by Mike Bostock
20 | ** Adjusted by Nadieh Bremer - July 2016 */
21 |
22 | /* global d3 */
23 | function loom() {
24 | var tau = Math.PI * 2;
25 |
26 | var padAngle = 0;
27 | var sortGroups = null;
28 | var sortSubgroups = null;
29 | var sortLooms = null;
30 | var emptyPerc = 0.2;
31 | var heightInner = 20;
32 | var widthInner = function widthInner() {
33 | return 30;
34 | };
35 | var value = function value(d) {
36 | return d.value;
37 | };
38 | var inner = function inner(d) {
39 | return d.inner;
40 | };
41 | var outer = function outer(d) {
42 | return d.outer;
43 | };
44 |
45 | function loomLayout(layoutData) {
46 | // Nest the data on the outer variable
47 | var data = d3.nest().key(outer).entries(layoutData);
48 |
49 | var n = data.length;
50 |
51 | // Loop over the outer groups and sum the values
52 |
53 | var groupSums = [];
54 | var groupIndex = d3.range(n);
55 | var subgroupIndex = [];
56 | var looms = [];
57 | looms.groups = new Array(n);
58 | var groups = looms.groups;
59 | var numSubGroups = void 0;
60 | looms.innergroups = [];
61 | var uniqueInner = looms.innergroups;
62 | var uniqueCheck = [];
63 | var k = void 0;
64 | var x = void 0;
65 | var x0 = void 0;
66 | var j = void 0;
67 | var l = void 0;
68 | var s = void 0;
69 | var v = void 0;
70 | var sum = void 0;
71 | var section = void 0;
72 | var remain = void 0;
73 | var counter = void 0;
74 | var reverseOrder = false;
75 | var approxCenter = void 0;
76 | k = 0;
77 | numSubGroups = 0;
78 | for (var i = 0; i < n; i += 1) {
79 | v = data[i].values.length;
80 | sum = 0;
81 | for (j = 0; j < v; j += 1) {
82 | sum += value(data[i].values[j]);
83 | } // for j
84 | groupSums.push(sum);
85 | subgroupIndex.push(d3.range(v));
86 | numSubGroups += v;
87 | k += sum;
88 | } // for i
89 |
90 | // Sort the groups…
91 | if (sortGroups) {
92 | groupIndex.sort(function (a, b) {
93 | return sortGroups(groupSums[a], groupSums[b]);
94 | });
95 | }
96 |
97 | // Sort subgroups…
98 | if (sortSubgroups) {
99 | subgroupIndex.forEach(function (d, i) {
100 | d.sort(function (a, b) {
101 | return sortSubgroups(inner(data[i].values[a]), inner(data[i].values[b]));
102 | });
103 | });
104 | }
105 |
106 | // After which group are we past the center, taking into account the padding
107 | // TODO: make something for if there is no "nice" split in two...
108 | var padk = k * (padAngle / tau);
109 | l = 0;
110 | for (var _i = 0; _i < n; _i += 1) {
111 | section = groupSums[groupIndex[_i]] + padk;
112 | l += section;
113 | if (l > (k + n * padk) / 2) {
114 | // Check if the group should be added to left or right
115 | remain = k + n * padk - (l - section);
116 | approxCenter = remain / section < 0.5 ? groupIndex[_i] : groupIndex[_i - 1];
117 | break;
118 | } // if
119 | } // for i
120 |
121 | // How much should be added to k to make the empty part emptyPerc big of the total
122 | var emptyk = k * emptyPerc / (1 - emptyPerc);
123 | k += emptyk;
124 |
125 | // Convert the sum to scaling factor for [0, 2pi].
126 | k = Math.max(0, tau - padAngle * n) / k;
127 | var dx = k ? padAngle : tau / n;
128 |
129 | // Compute the start and end angle for each group and subgroup.
130 | // Note: Opera has a bug reordering object literal properties!
131 | var subgroups = new Array(numSubGroups);
132 | x = emptyk * 0.25 * k; // starting with quarter of the empty part to the side;
133 | counter = 0;
134 | for (var _i2 = 0; _i2 < n; _i2 += 1) {
135 | var di = groupIndex[_i2];
136 | var outername = data[di].key;
137 |
138 | x0 = x;
139 | s = subgroupIndex[di].length;
140 | for (j = 0; j < s; j += 1) {
141 | var dj = reverseOrder ? subgroupIndex[di][s - 1 - j] : subgroupIndex[di][j];
142 |
143 | v = value(data[di].values[dj]);
144 | var innername = inner(data[di].values[dj]);
145 | var a0 = x;
146 | x += v * k;
147 | var a1 = x;
148 | subgroups[counter] = {
149 | index: di,
150 | subindex: dj,
151 | startAngle: a0,
152 | endAngle: a1,
153 | value: v,
154 | outername: outername,
155 | innername: innername,
156 | groupStartAngle: x0
157 | };
158 |
159 | // Check and save the unique inner names
160 | if (!uniqueCheck[innername]) {
161 | uniqueCheck[innername] = true;
162 | uniqueInner.push({ name: innername });
163 | } // if
164 |
165 | counter += 1;
166 | } // for j
167 | groups[di] = {
168 | index: di,
169 | startAngle: x0,
170 | endAngle: x,
171 | value: groupSums[di],
172 | outername: outername
173 | };
174 | x += dx;
175 | // If this is the approximate center, add half of the empty piece for the bottom
176 | if (approxCenter === di) x += emptyk * 0.5 * k;
177 | // If you've crossed the bottom, reverse the order of the inner strings
178 | if (x > Math.PI) reverseOrder = true;
179 | } // for i
180 |
181 | // Sort the inner groups in the same way as the strings
182 | if (sortSubgroups) {
183 | uniqueInner.sort(function (a, b) {
184 | return sortSubgroups(a.name, b.name);
185 | });
186 | }
187 |
188 | // Find x and y locations of the inner categories
189 | var m = uniqueInner.length;
190 | for (var _i3 = 0; _i3 < m; _i3 += 1) {
191 | uniqueInner[_i3].x = 0;
192 | uniqueInner[_i3].y = -m * heightInner / 2 + _i3 * heightInner;
193 | uniqueInner[_i3].offset = widthInner(uniqueInner[_i3].name, _i3);
194 | } // for i
195 |
196 | // Generate bands for each (non-empty) subgroup-subgroup link
197 | counter = 0;
198 | for (var _i4 = 0; _i4 < n; _i4 += 1) {
199 | var _di = groupIndex[_i4];
200 | s = subgroupIndex[_di].length;
201 | for (j = 0; j < s; j += 1) {
202 | var outerGroup = subgroups[counter];
203 | var innerTerm = outerGroup.innername;
204 | // Find the correct inner object based on the name
205 | var innerGroup = searchTerm(innerTerm, 'name', uniqueInner);
206 | if (outerGroup.value) {
207 | looms.push({ inner: innerGroup, outer: outerGroup });
208 | } // if
209 | counter += 1;
210 | } // for j
211 | } // for i
212 |
213 | return sortLooms ? looms.sort(sortLooms) : looms;
214 | } // loomLayout
215 |
216 | function searchTerm(term, property, arrayToSearch) {
217 | for (var i = 0; i < arrayToSearch.length; i += 1) {
218 | if (arrayToSearch[i][property] === term) {
219 | return arrayToSearch[i];
220 | } // if
221 | } // for i
222 | return null;
223 | } // searchTerm
224 |
225 | loomLayout.padAngle = function (_) {
226 | return arguments.length ? (padAngle = Math.max(0, _), loomLayout) : padAngle;
227 | };
228 |
229 | loomLayout.inner = function (_) {
230 | return arguments.length ? (inner = _, loomLayout) : inner;
231 | };
232 |
233 | loomLayout.outer = function (_) {
234 | return arguments.length ? (outer = _, loomLayout) : outer;
235 | };
236 |
237 | loomLayout.value = function (_) {
238 | return arguments.length ? (value = _, loomLayout) : value;
239 | };
240 |
241 | loomLayout.heightInner = function (_) {
242 | return arguments.length ? (heightInner = _, loomLayout) : heightInner;
243 | };
244 |
245 | loomLayout.widthInner = function (_) {
246 | return arguments.length ? (widthInner = typeof _ === 'function' ? _ : constant(+_), loomLayout) : widthInner;
247 | };
248 |
249 | loomLayout.emptyPerc = function (_) {
250 | return arguments.length ? (emptyPerc = _ < 1 ? Math.max(0, _) : Math.max(0, _ * 0.01), loomLayout) : emptyPerc;
251 | };
252 |
253 | loomLayout.sortGroups = function (_) {
254 | return arguments.length ? (sortGroups = _, loomLayout) : sortGroups;
255 | };
256 |
257 | loomLayout.sortSubgroups = function (_) {
258 | return arguments.length ? (sortSubgroups = _, loomLayout) : sortSubgroups;
259 | };
260 |
261 | loomLayout.sortLooms = function (_) {
262 | return arguments.length ? (_ == null ? sortLooms = null : (sortLooms = compareValue(_))._ = _, loomLayout) : sortLooms && sortLooms._;
263 | };
264 |
265 | return loomLayout;
266 | } // loom
267 |
268 | /* global d3 */
269 |
270 | function string() {
271 | var slice = Array.prototype.slice;
272 | var cos = Math.cos;
273 | var sin = Math.sin;
274 | var halfPi = Math.PI / 2;
275 | var tau = Math.PI * 2;
276 |
277 | var inner = function inner(d) {
278 | return d.inner;
279 | };
280 | var outer = function outer(d) {
281 | return d.outer;
282 | };
283 | var radius = function radius() {
284 | return 100;
285 | };
286 | var groupStartAngle = function groupStartAngle(d) {
287 | return d.groupStartAngle;
288 | };
289 | var startAngle = function startAngle(d) {
290 | return d.startAngle;
291 | };
292 | var endAngle = function endAngle(d) {
293 | return d.endAngle;
294 | };
295 | var x = function x(d) {
296 | return d.x;
297 | };
298 | var y = function y(d) {
299 | return d.y;
300 | };
301 | var offset = function offset(d) {
302 | return d.offset;
303 | };
304 | var pullout = 50;
305 | var thicknessInner = 0;
306 | var context = null;
307 |
308 | function stringLayout() {
309 | var buffer = void 0;
310 |
311 | for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
312 | args[_key] = arguments[_key];
313 | }
314 |
315 | var argv = slice.call(args);
316 | var out = outer.apply(this, argv);
317 | var inn = inner.apply(this, argv);
318 | argv[0] = out;
319 | var sr = +radius.apply(this, argv);
320 | var sa0 = startAngle.apply(this, argv) - halfPi;
321 | var sga0 = groupStartAngle.apply(this, argv) - halfPi;
322 | var sa1 = endAngle.apply(this, argv) - halfPi;
323 | var sx0 = sr * cos(sa0);
324 | var sy0 = sr * sin(sa0);
325 | var sx1 = sr * cos(sa1);
326 | var sy1 = sr * sin(sa1);
327 | argv[0] = inn;
328 | // 'tr' is assigned a value but never used
329 | // const tr = +radius.apply(this, (argv));
330 | var tx = x.apply(this, argv);
331 | var ty = y.apply(this, argv);
332 | var toffset = offset.apply(this, argv);
333 | var xco = void 0;
334 | var yco = void 0;
335 | var xci = void 0;
336 | var yci = void 0;
337 |
338 | // Does the group lie on the left side;
339 | var leftHalf = sga0 + halfPi > Math.PI && sga0 + halfPi < tau;
340 | // If the group lies on the other side, switch the inner point offset
341 | if (leftHalf) toffset = -toffset;
342 | tx += toffset;
343 | // And the height of the end point
344 | var theight = leftHalf ? -thicknessInner : thicknessInner;
345 |
346 | if (!context) {
347 | buffer = d3.path();
348 | context = buffer;
349 | }
350 |
351 | // Change the pullout based on where the stringLayout is
352 | var pulloutContext = (leftHalf ? -1 : 1) * pullout;
353 | sx0 += pulloutContext;
354 | sx1 += pulloutContext;
355 | // Start at smallest angle of outer arc
356 | context.moveTo(sx0, sy0);
357 | // Circular part along the outer arc
358 | context.arc(pulloutContext, 0, sr, sa0, sa1);
359 | // From end outer arc to center (taking into account the pullout)
360 | xco = d3.interpolateNumber(pulloutContext, sx1)(0.5);
361 | yco = d3.interpolateNumber(0, sy1)(0.5);
362 | if (!leftHalf && sx1 < tx || leftHalf && sx1 > tx) {
363 | // If the outer point lies closer to the center than the inner point
364 | xci = tx + (tx - sx1) / 2;
365 | yci = d3.interpolateNumber(ty + theight / 2, sy1)(0.5);
366 | } else {
367 | xci = d3.interpolateNumber(tx, sx1)(0.25);
368 | yci = ty + theight / 2;
369 | } // else
370 | context.bezierCurveTo(xco, yco, xci, yci, tx, ty + theight / 2);
371 | // Draw a straight line up/down (depending on the side of the circle)
372 | context.lineTo(tx, ty - theight / 2);
373 | // From center (taking into account the pullout) to start of outer arc
374 | xco = d3.interpolateNumber(pulloutContext, sx0)(0.5);
375 | yco = d3.interpolateNumber(0, sy0)(0.5);
376 | if (!leftHalf && sx0 < tx || leftHalf && sx0 > tx) {
377 | // If the outer point lies closer to the center than the inner point
378 | xci = tx + (tx - sx0) / 2;
379 | yci = d3.interpolateNumber(ty - theight / 2, sy0)(0.5);
380 | } else {
381 | xci = d3.interpolateNumber(tx, sx0)(0.25);
382 | yci = ty - theight / 2;
383 | } // else
384 | context.bezierCurveTo(xci, yci, xco, yco, sx0, sy0);
385 | // Close path
386 | context.closePath();
387 |
388 | if (buffer) {
389 | context = null;
390 | return '' + buffer || null;
391 | }
392 | return null;
393 | }
394 |
395 | stringLayout.radius = function (_) {
396 | return arguments.length ? (radius = typeof _ === 'function' ? _ : constant(+_), stringLayout) : radius;
397 | };
398 |
399 | stringLayout.groupStartAngle = function (_) {
400 | return arguments.length ? (groupStartAngle = typeof _ === 'function' ? _ : constant(+_), stringLayout) : groupStartAngle;
401 | };
402 |
403 | stringLayout.startAngle = function (_) {
404 | return arguments.length ? (startAngle = typeof _ === 'function' ? _ : constant(+_), stringLayout) : startAngle;
405 | };
406 |
407 | stringLayout.endAngle = function (_) {
408 | return arguments.length ? (endAngle = typeof _ === 'function' ? _ : constant(+_), stringLayout) : endAngle;
409 | };
410 |
411 | stringLayout.x = function (_) {
412 | return arguments.length ? (x = _, stringLayout) : x;
413 | };
414 |
415 | stringLayout.y = function (_) {
416 | return arguments.length ? (y = _, stringLayout) : y;
417 | };
418 |
419 | stringLayout.offset = function (_) {
420 | return arguments.length ? (offset = _, stringLayout) : offset;
421 | };
422 |
423 | stringLayout.thicknessInner = function (_) {
424 | return arguments.length ? (thicknessInner = _, stringLayout) : thicknessInner;
425 | };
426 |
427 | stringLayout.inner = function (_) {
428 | return arguments.length ? (inner = _, stringLayout) : inner;
429 | };
430 |
431 | stringLayout.outer = function (_) {
432 | return arguments.length ? (outer = _, stringLayout) : outer;
433 | };
434 |
435 | stringLayout.pullout = function (_) {
436 | return arguments.length ? (pullout = _, stringLayout) : pullout;
437 | };
438 |
439 | stringLayout.context = function (_) {
440 | return arguments.length ? (context = _ == null ? null : _, stringLayout) : context;
441 | };
442 |
443 | return stringLayout;
444 | }
445 |
446 | exports.loom = loom;
447 | exports.string = string;
448 |
449 | Object.defineProperty(exports, '__esModule', { value: true });
450 |
451 | })));
452 | //# sourceMappingURL=d3-loom.js.map
453 |
--------------------------------------------------------------------------------
/build/d3-loom.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"d3-loom.js","sources":["../src/compare-value.js","../src/constant.js","../src/loom.js","../src/string.js"],"sourcesContent":["export default function compareValue(compare) {\n return (a, b) => compare(a.outer.value, b.outer.value);\n}\n","export default function constant(x) {\n return () => x;\n}\n","/* Based on the d3v4 d3.chord() function by Mike Bostock\n** Adjusted by Nadieh Bremer - July 2016 */\n\n/* global d3 */\nimport compareValue from './compare-value';\nimport constant from './constant';\n\nexport default function loom() {\n const tau = Math.PI * 2;\n\n let padAngle = 0;\n let sortGroups = null;\n let sortSubgroups = null;\n let sortLooms = null;\n let emptyPerc = 0.2;\n let heightInner = 20;\n let widthInner = () => 30;\n let value = d => d.value;\n let inner = d => d.inner;\n let outer = d => d.outer;\n\n function loomLayout(layoutData) {\n // Nest the data on the outer variable\n const data = d3.nest().key(outer).entries(layoutData);\n\n const n = data.length;\n\n // Loop over the outer groups and sum the values\n\n const groupSums = [];\n const groupIndex = d3.range(n);\n const subgroupIndex = [];\n const looms = [];\n looms.groups = new Array(n);\n const groups = looms.groups;\n let numSubGroups;\n looms.innergroups = [];\n const uniqueInner = looms.innergroups;\n const uniqueCheck = [];\n let k;\n let x;\n let x0;\n let j;\n let l;\n let s;\n let v;\n let sum;\n let section;\n let remain;\n let counter;\n let reverseOrder = false;\n let approxCenter;\n k = 0;\n numSubGroups = 0;\n for (let i = 0; i < n; i += 1) {\n v = data[i].values.length;\n sum = 0;\n for (j = 0; j < v; j += 1) {\n sum += value(data[i].values[j]);\n } // for j\n groupSums.push(sum);\n subgroupIndex.push(d3.range(v));\n numSubGroups += v;\n k += sum;\n } // for i\n\n // Sort the groups…\n if (sortGroups) {\n groupIndex.sort((a, b) => sortGroups(groupSums[a], groupSums[b]));\n }\n\n // Sort subgroups…\n if (sortSubgroups) {\n subgroupIndex.forEach((d, i) => {\n d.sort((a, b) =>\n sortSubgroups(inner(data[i].values[a]), inner(data[i].values[b]))\n );\n });\n }\n\n // After which group are we past the center, taking into account the padding\n // TODO: make something for if there is no \"nice\" split in two...\n const padk = k * (padAngle / tau);\n l = 0;\n for (let i = 0; i < n; i += 1) {\n section = groupSums[groupIndex[i]] + padk;\n l += section;\n if (l > (k + n * padk) / 2) {\n // Check if the group should be added to left or right\n remain = k + n * padk - (l - section);\n approxCenter = remain / section < 0.5\n ? groupIndex[i]\n : groupIndex[i - 1];\n break;\n } // if\n } // for i\n\n // How much should be added to k to make the empty part emptyPerc big of the total\n const emptyk = k * emptyPerc / (1 - emptyPerc);\n k += emptyk;\n\n // Convert the sum to scaling factor for [0, 2pi].\n k = Math.max(0, tau - padAngle * n) / k;\n const dx = k ? padAngle : tau / n;\n\n // Compute the start and end angle for each group and subgroup.\n // Note: Opera has a bug reordering object literal properties!\n const subgroups = new Array(numSubGroups);\n x = emptyk * 0.25 * k; // starting with quarter of the empty part to the side;\n counter = 0;\n for (let i = 0; i < n; i += 1) {\n const di = groupIndex[i];\n const outername = data[di].key;\n\n x0 = x;\n s = subgroupIndex[di].length;\n for (j = 0; j < s; j += 1) {\n const dj = reverseOrder\n ? subgroupIndex[di][s - 1 - j]\n : subgroupIndex[di][j];\n\n v = value(data[di].values[dj]);\n const innername = inner(data[di].values[dj]);\n const a0 = x;\n x += v * k;\n const a1 = x;\n subgroups[counter] = {\n index: di,\n subindex: dj,\n startAngle: a0,\n endAngle: a1,\n value: v,\n outername,\n innername,\n groupStartAngle: x0\n };\n\n // Check and save the unique inner names\n if (!uniqueCheck[innername]) {\n uniqueCheck[innername] = true;\n uniqueInner.push({ name: innername });\n } // if\n\n counter += 1;\n } // for j\n groups[di] = {\n index: di,\n startAngle: x0,\n endAngle: x,\n value: groupSums[di],\n outername\n };\n x += dx;\n // If this is the approximate center, add half of the empty piece for the bottom\n if (approxCenter === di) x += emptyk * 0.5 * k;\n // If you've crossed the bottom, reverse the order of the inner strings\n if (x > Math.PI) reverseOrder = true;\n } // for i\n\n // Sort the inner groups in the same way as the strings\n if (sortSubgroups) {\n uniqueInner.sort((a, b) => sortSubgroups(a.name, b.name));\n }\n\n // Find x and y locations of the inner categories\n const m = uniqueInner.length;\n for (let i = 0; i < m; i += 1) {\n uniqueInner[i].x = 0;\n uniqueInner[i].y = -m * heightInner / 2 + i * heightInner;\n uniqueInner[i].offset = widthInner(uniqueInner[i].name, i);\n } // for i\n\n // Generate bands for each (non-empty) subgroup-subgroup link\n counter = 0;\n for (let i = 0; i < n; i += 1) {\n const di = groupIndex[i];\n s = subgroupIndex[di].length;\n for (j = 0; j < s; j += 1) {\n const outerGroup = subgroups[counter];\n const innerTerm = outerGroup.innername;\n // Find the correct inner object based on the name\n const innerGroup = searchTerm(innerTerm, 'name', uniqueInner);\n if (outerGroup.value) {\n looms.push({ inner: innerGroup, outer: outerGroup });\n } // if\n counter += 1;\n } // for j\n } // for i\n\n return sortLooms ? looms.sort(sortLooms) : looms;\n } // loomLayout\n\n function searchTerm(term, property, arrayToSearch) {\n for (let i = 0; i < arrayToSearch.length; i += 1) {\n if (arrayToSearch[i][property] === term) {\n return arrayToSearch[i];\n } // if\n } // for i\n return null;\n } // searchTerm\n\n loomLayout.padAngle = function(_) {\n return arguments.length\n ? ((padAngle = Math.max(0, _)), loomLayout)\n : padAngle;\n };\n\n loomLayout.inner = function(_) {\n return arguments.length ? ((inner = _), loomLayout) : inner;\n };\n\n loomLayout.outer = function(_) {\n return arguments.length ? ((outer = _), loomLayout) : outer;\n };\n\n loomLayout.value = function(_) {\n return arguments.length ? ((value = _), loomLayout) : value;\n };\n\n loomLayout.heightInner = function(_) {\n return arguments.length ? ((heightInner = _), loomLayout) : heightInner;\n };\n\n loomLayout.widthInner = function(_) {\n return arguments.length\n ? ((widthInner = typeof _ === 'function' ? _ : constant(+_)), loomLayout)\n : widthInner;\n };\n\n loomLayout.emptyPerc = function(_) {\n return arguments.length\n ? ((emptyPerc = _ < 1\n ? Math.max(0, _)\n : Math.max(0, _ * 0.01)), loomLayout)\n : emptyPerc;\n };\n\n loomLayout.sortGroups = function(_) {\n return arguments.length ? ((sortGroups = _), loomLayout) : sortGroups;\n };\n\n loomLayout.sortSubgroups = function(_) {\n return arguments.length ? ((sortSubgroups = _), loomLayout) : sortSubgroups;\n };\n\n loomLayout.sortLooms = function(_) {\n return arguments.length\n ? (_ == null\n ? (sortLooms = null)\n : ((sortLooms = compareValue(_))._ = _), loomLayout)\n : sortLooms && sortLooms._;\n };\n\n return loomLayout;\n} // loom\n","/* global d3 */\n\nimport constant from './constant';\n\nexport default function string() {\n const slice = Array.prototype.slice;\n const cos = Math.cos;\n const sin = Math.sin;\n const halfPi = Math.PI / 2;\n const tau = Math.PI * 2;\n\n let inner = d => d.inner;\n let outer = d => d.outer;\n let radius = () => 100;\n let groupStartAngle = d => d.groupStartAngle;\n let startAngle = d => d.startAngle;\n let endAngle = d => d.endAngle;\n let x = d => d.x;\n let y = d => d.y;\n let offset = d => d.offset;\n let pullout = 50;\n let thicknessInner = 0;\n let context = null;\n\n function stringLayout(...args) {\n let buffer;\n const argv = slice.call(args);\n const out = outer.apply(this, argv);\n const inn = inner.apply(this, argv);\n argv[0] = out;\n const sr = +radius.apply(this, argv);\n const sa0 = startAngle.apply(this, argv) - halfPi;\n const sga0 = groupStartAngle.apply(this, argv) - halfPi;\n const sa1 = endAngle.apply(this, argv) - halfPi;\n let sx0 = sr * cos(sa0);\n const sy0 = sr * sin(sa0);\n let sx1 = sr * cos(sa1);\n const sy1 = sr * sin(sa1);\n argv[0] = inn;\n // 'tr' is assigned a value but never used\n // const tr = +radius.apply(this, (argv));\n let tx = x.apply(this, argv);\n const ty = y.apply(this, argv);\n let toffset = offset.apply(this, argv);\n let xco;\n let yco;\n let xci;\n let yci;\n\n // Does the group lie on the left side;\n const leftHalf = sga0 + halfPi > Math.PI && sga0 + halfPi < tau;\n // If the group lies on the other side, switch the inner point offset\n if (leftHalf) toffset = -toffset;\n tx += toffset;\n // And the height of the end point\n const theight = leftHalf ? -thicknessInner : thicknessInner;\n\n if (!context) {\n buffer = d3.path();\n context = buffer;\n }\n\n // Change the pullout based on where the stringLayout is\n const pulloutContext = (leftHalf ? -1 : 1) * pullout;\n sx0 += pulloutContext;\n sx1 += pulloutContext;\n // Start at smallest angle of outer arc\n context.moveTo(sx0, sy0);\n // Circular part along the outer arc\n context.arc(pulloutContext, 0, sr, sa0, sa1);\n // From end outer arc to center (taking into account the pullout)\n xco = d3.interpolateNumber(pulloutContext, sx1)(0.5);\n yco = d3.interpolateNumber(0, sy1)(0.5);\n if ((!leftHalf && sx1 < tx) || (leftHalf && sx1 > tx)) {\n // If the outer point lies closer to the center than the inner point\n xci = tx + (tx - sx1) / 2;\n yci = d3.interpolateNumber(ty + theight / 2, sy1)(0.5);\n } else {\n xci = d3.interpolateNumber(tx, sx1)(0.25);\n yci = ty + theight / 2;\n } // else\n context.bezierCurveTo(xco, yco, xci, yci, tx, ty + theight / 2);\n // Draw a straight line up/down (depending on the side of the circle)\n context.lineTo(tx, ty - theight / 2);\n // From center (taking into account the pullout) to start of outer arc\n xco = d3.interpolateNumber(pulloutContext, sx0)(0.5);\n yco = d3.interpolateNumber(0, sy0)(0.5);\n if ((!leftHalf && sx0 < tx) || (leftHalf && sx0 > tx)) {\n // If the outer point lies closer to the center than the inner point\n xci = tx + (tx - sx0) / 2;\n yci = d3.interpolateNumber(ty - theight / 2, sy0)(0.5);\n } else {\n xci = d3.interpolateNumber(tx, sx0)(0.25);\n yci = ty - theight / 2;\n } // else\n context.bezierCurveTo(xci, yci, xco, yco, sx0, sy0);\n // Close path\n context.closePath();\n\n if (buffer) {\n context = null;\n return `${buffer}` || null;\n }\n return null;\n }\n\n stringLayout.radius = function(_) {\n return arguments.length\n ? ((radius = typeof _ === 'function' ? _ : constant(+_)), stringLayout)\n : radius;\n };\n\n stringLayout.groupStartAngle = function(_) {\n return arguments.length\n ? ((groupStartAngle = typeof _ === 'function'\n ? _\n : constant(+_)), stringLayout)\n : groupStartAngle;\n };\n\n stringLayout.startAngle = function(_) {\n return arguments.length\n ? ((startAngle = typeof _ === 'function'\n ? _\n : constant(+_)), stringLayout)\n : startAngle;\n };\n\n stringLayout.endAngle = function(_) {\n return arguments.length\n ? ((endAngle = typeof _ === 'function' ? _ : constant(+_)), stringLayout)\n : endAngle;\n };\n\n stringLayout.x = function(_) {\n return arguments.length ? ((x = _), stringLayout) : x;\n };\n\n stringLayout.y = function(_) {\n return arguments.length ? ((y = _), stringLayout) : y;\n };\n\n stringLayout.offset = function(_) {\n return arguments.length ? ((offset = _), stringLayout) : offset;\n };\n\n stringLayout.thicknessInner = function(_) {\n return arguments.length\n ? ((thicknessInner = _), stringLayout)\n : thicknessInner;\n };\n\n stringLayout.inner = function(_) {\n return arguments.length ? ((inner = _), stringLayout) : inner;\n };\n\n stringLayout.outer = function(_) {\n return arguments.length ? ((outer = _), stringLayout) : outer;\n };\n\n stringLayout.pullout = function(_) {\n return arguments.length ? ((pullout = _), stringLayout) : pullout;\n };\n\n stringLayout.context = function(_) {\n return arguments.length\n ? ((context = _ == null ? null : _), stringLayout)\n : context;\n };\n\n return stringLayout;\n}\n"],"names":["compareValue","compare","a","b","outer","value","constant","x","loom","tau","Math","PI","padAngle","sortGroups","sortSubgroups","sortLooms","emptyPerc","heightInner","widthInner","d","inner","loomLayout","layoutData","data","d3","nest","key","entries","n","length","groupSums","groupIndex","range","subgroupIndex","looms","groups","Array","numSubGroups","innergroups","uniqueInner","uniqueCheck","k","x0","j","l","s","v","sum","section","remain","counter","reverseOrder","approxCenter","i","values","push","sort","forEach","padk","emptyk","max","dx","subgroups","di","outername","dj","innername","a0","a1","name","m","y","offset","outerGroup","innerTerm","innerGroup","searchTerm","term","property","arrayToSearch","_","arguments","string","slice","prototype","cos","sin","halfPi","radius","groupStartAngle","startAngle","endAngle","pullout","thicknessInner","context","stringLayout","buffer","args","argv","call","out","apply","inn","sr","sa0","sga0","sa1","sx0","sy0","sx1","sy1","tx","ty","toffset","xco","yco","xci","yci","leftHalf","theight","path","pulloutContext","moveTo","arc","interpolateNumber","bezierCurveTo","lineTo","closePath"],"mappings":"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"}
--------------------------------------------------------------------------------
/build/d3-loom.min.js:
--------------------------------------------------------------------------------
1 | !function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(n.d3=n.d3||{})}(this,function(n){"use strict";function t(n){return function(t,e){return n(t.outer.value,e.outer.value)}}function e(n){return function(){return n}}function r(){function n(n){var t=d3.nest().key(p).entries(n),e=t.length,g=[],d=d3.range(e),y=[],m=[];m.groups=new Array(e);var A=m.groups,b=void 0;m.innergroups=[];var x=m.innergroups,M=[],I=void 0,N=void 0,P=void 0,k=void 0,S=void 0,T=void 0,_=void 0,w=void 0,j=void 0,z=void 0,C=void 0,E=!1,G=void 0;I=0,b=0;for(var L=0;L(I+e*O)/2){z=I+e*O-(S-j),G=z/j<.5?d[q]:d[q-1];break}var B=I*f/(1-f);I+=B,I=Math.max(0,u-o*e)/I;var D=I?o:u/e,F=new Array(b);N=.25*B*I,C=0;for(var H=0;HMath.PI&&(E=!0)}l&&x.sort(function(n,t){return l(n.name,t.name)});for(var W=x.length,X=0;XMath.PI&&k+oz?(O=z+(z-w)/2,q=d3.interpolateNumber(C+D/2,j)(.5)):(O=d3.interpolateNumber(z,w)(.25),q=C+D/2),m.bezierCurveTo(G,L,O,q,z,C+D/2),m.lineTo(z,C-D/2),G=d3.interpolateNumber(F,T)(.5),L=d3.interpolateNumber(0,_)(.5),!B&&Tz?(O=z+(z-T)/2,q=d3.interpolateNumber(C-D/2,_)(.5)):(O=d3.interpolateNumber(z,T)(.25),q=C-D/2),m.bezierCurveTo(O,q,G,L,T,_),m.closePath(),n?(m=null,""+n||null):null}var t=Array.prototype.slice,r=Math.cos,u=Math.sin,o=Math.PI/2,i=2*Math.PI,l=function(n){return n.inner},a=function(n){return n.outer},f=function(){return 100},c=function(n){return n.groupStartAngle},h=function(n){return n.startAngle},v=function(n){return n.endAngle},s=function(n){return n.x},p=function(n){return n.y},g=function(n){return n.offset},d=50,y=0,m=null;return n.radius=function(t){return arguments.length?(f="function"==typeof t?t:e(+t),n):f},n.groupStartAngle=function(t){return arguments.length?(c="function"==typeof t?t:e(+t),n):c},n.startAngle=function(t){return arguments.length?(h="function"==typeof t?t:e(+t),n):h},n.endAngle=function(t){return arguments.length?(v="function"==typeof t?t:e(+t),n):v},n.x=function(t){return arguments.length?(s=t,n):s},n.y=function(t){return arguments.length?(p=t,n):p},n.offset=function(t){return arguments.length?(g=t,n):g},n.thicknessInner=function(t){return arguments.length?(y=t,n):y},n.inner=function(t){return arguments.length?(l=t,n):l},n.outer=function(t){return arguments.length?(a=t,n):a},n.pullout=function(t){return arguments.length?(d=t,n):d},n.context=function(t){return arguments.length?(m=null==t?null:t,n):m},n}n.loom=r,n.string=u,Object.defineProperty(n,"__esModule",{value:!0})});
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
211 |
212 |