/*Based on the d3v4 d3.chord() function by Mike Bostock ** Adjusted by Nadieh Bremer - July 2016 */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-collection'), require('d3-array'), require('d3-interpolate'), require('d3-path')) : typeof define === 'function' && define.amd ? define(['exports', 'd3-collection', 'd3-array', 'd3-interpolate', 'd3-path'], factory) : (factory((global.d3 = global.d3 || {}),global.d3,global.d3,global.d3,global.d3)); }(this, function (exports,d3Collection,d3Array,d3Interpolate,d3Path) { 'use strict'; function loom(data) { var pi$3 = Math.PI; var tau$3 = pi$3 * 2; var max$1 = Math.max; var padAngle = 0, sortGroups = null, sortSubgroups = null, sortLooms = null, emptyPerc = 0.2, heightInner = 20, widthInner = function() { return 30; }, value = function(d) { return d.value; }, inner = function(d) { return d.inner; }, outer = function(d) { return d.outer; }; function loom(data) { //Nest the data on the outer variable data = d3.nest().key(outer).entries(data); var n = data.length, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], looms = [], groups = looms.groups = new Array(n), subgroups, numSubGroups, uniqueInner = looms.innergroups = [], uniqueCheck = [], emptyk, k, x, x0, dx, i, j, l, m, s, v, sum, counter, reverseOrder = false, approxCenter; //Loop over the outer groups and sum the values k = 0; numSubGroups = 0; for(i = 0; i < n; i++) { v = data[i].values.length; sum = 0; for(j = 0; j < v; j++) { sum += value(data[i].values[j]); }//for j groupSums.push(sum); subgroupIndex.push(d3.range(v)); numSubGroups += v; k += sum; }//for i // Sort the groups… if (sortGroups) groupIndex.sort(function(a, b) { return sortGroups(groupSums[a], groupSums[b]); }); // Sort subgroups… if (sortSubgroups) subgroupIndex.forEach(function(d, i) { d.sort(function(a, b) { return sortSubgroups( inner(data[i].values[a]), inner(data[i].values[b]) ); }); }); //After which group are we past the center //TODO: make something for if there is no nice split in two... l = 0; for(i = 0; i < n; i++) { l += groupSums[groupIndex[i]]; if(l > k/2) { approxCenter = groupIndex[i]; break; }//if }//for i //How much should be added to k to make the empty part emptyPerc big of the total emptyk = k * emptyPerc / (1 - emptyPerc); k += emptyk; // Convert the sum to scaling factor for [0, 2pi]. k = max$1(0, tau$3 - padAngle * n) / k; dx = k ? padAngle : tau$3 / n; // Compute the start and end angle for each group and subgroup. // Note: Opera has a bug reordering object literal properties! subgroups = new Array(numSubGroups); x = emptyk * 0.25 * k; //quarter of the empty part //0; counter = 0; for(i = 0; i < n; i++) { var di = groupIndex[i], outername = data[di].key; if(approxCenter === di) { x = x + emptyk * 0.5 * k; }//if x0 = x; //If you've crossed the bottom, reverse the order of the inner strings if(x > pi$3) reverseOrder = true; s = subgroupIndex[di].length; for(j = 0; j < s; j++) { var dj = reverseOrder ? subgroupIndex[di][(s-1)-j] : subgroupIndex[di][j], v = value(data[di].values[dj]), innername = inner(data[di].values[dj]), a0 = x, a1 = x += v * k; subgroups[counter] = { index: di, subindex: dj, startAngle: a0, endAngle: a1, value: v, outername: outername, innername: innername }; //Check and save the unique inner names if( !uniqueCheck[innername] ) { uniqueCheck[innername] = true; uniqueInner.push({name: innername}); }//if counter += 1; }//for j groups[di] = { index: di, startAngle: x0, endAngle: x, value: groupSums[di], outername: outername }; x += dx; }//for i //Sort the inner groups in the same way as the strings uniqueInner.sort(function(a, b) { return sortSubgroups( a.name, b.name ); }); //Find x and y locations of the inner categories //TODO: make x depend on length of inner name m = uniqueInner.length for(i = 0; i < m; i++) { uniqueInner[i].x = 0; uniqueInner[i].y = -m*heightInner/2 + i*heightInner; uniqueInner[i].offset = widthInner(uniqueInner[i].name, i, uniqueInner); }//for i //Generate bands for each (non-empty) subgroup-subgroup link counter = 0; for(i = 0; i < n; i++) { var di = groupIndex[i]; s = subgroupIndex[di].length; for(j = 0; j < s; j++) { var outerGroup = subgroups[counter]; var innerTerm = outerGroup.innername; //Find the correct inner object based on the name var innerGroup = searchTerm(innerTerm, "name", uniqueInner); if (outerGroup.value) { looms.push({inner: innerGroup, outer: outerGroup}); }//if counter +=1; }//for j }//for i return sortLooms ? looms.sort(sortLooms) : looms; }//function loom function searchTerm(term, property, arrayToSearch){ for (var i=0; i < arrayToSearch.length; i++) { if (arrayToSearch[i][property] === term) { return arrayToSearch[i]; }//if }//for i }//searchTerm function constant$11(x) { return function() { return x; }; } loom.padAngle = function(_) { return arguments.length ? (padAngle = max$1(0, _), loom) : padAngle; }; loom.inner = function(_) { return arguments.length ? (inner = _, loom) : inner; }; loom.outer = function(_) { return arguments.length ? (outer = _, loom) : outer; }; loom.value = function(_) { return arguments.length ? (value = _, loom) : value; }; loom.heightInner = function(_) { return arguments.length ? (heightInner = _, loom) : heightInner; }; loom.widthInner = function(_) { return arguments.length ? (widthInner = typeof _ === "function" ? _ : constant$11(+_), loom) : widthInner; }; loom.emptyPerc = function(_) { return arguments.length ? (emptyPerc = _ < 1 ? max$1(0, _) : max$1(0, _*0.01), loom) : emptyPerc; }; loom.sortGroups = function(_) { return arguments.length ? (sortGroups = _, loom) : sortGroups; }; loom.sortSubgroups = function(_) { return arguments.length ? (sortSubgroups = _, loom) : sortSubgroups; }; loom.sortLooms = function(_) { return arguments.length ? (_ == null ? sortLooms = null : (sortLooms = compareValue(_))._ = _, loom) : sortLooms && sortLooms._; }; return loom; }//loom function string() { var slice$5 = Array.prototype.slice; var cos = Math.cos; var sin = Math.sin; var pi$3 = Math.PI; var halfPi$2 = pi$3 / 2; var tau$3 = pi$3 * 2; var max$1 = Math.max; var inner = function (d) { return d.inner; }, outer = function (d) { return d.outer; }, radius = function (d) { return 100; }, startAngle = function (d) { return d.startAngle; }, endAngle = function (d) { return d.endAngle; }, x = function (d) { return d.x; }, y = function (d) { return d.y; }, offset = function (d) { return d.offset; }, pullout = 50, thicknessInner = 0, context = null; function string() { var buffer, argv = slice$5.call(arguments), out = outer.apply(this, argv), inn = inner.apply(this, argv), sr = +radius.apply(this, (argv[0] = out, argv)), sa0 = startAngle.apply(this, argv) - halfPi$2, sa1 = endAngle.apply(this, argv) - halfPi$2, sx0 = sr * cos(sa0), sy0 = sr * sin(sa0), sx1 = sr * cos(sa1), sy1 = sr * sin(sa1), tr = +radius.apply(this, (argv[0] = inn, argv)), tx = x.apply(this, argv), ty = y.apply(this, argv), toffset = offset.apply(this, argv), theight, xco, yco, xci, yci, leftHalf, pulloutContext; //Does the group lie on the left side leftHalf = sa0+halfPi$2 > pi$3 && sa0+halfPi$2 < tau$3; //If the group lies on the other side, switch the inner point offset if(leftHalf) toffset = -toffset; tx = tx + toffset; //And the height of the end point theight = leftHalf ? -thicknessInner : thicknessInner; if (!context) context = buffer = d3.path(); //Change the pullout based on where the string is pulloutContext = (leftHalf ? -1 : 1 ) * pullout; sx0 = sx0 + pulloutContext; sx1 = sx1 + pulloutContext; //Start at smallest angle of outer arc context.moveTo(sx0, sy0); //Circular part along the outer arc context.arc(pulloutContext, 0, sr, sa0, sa1); //From end outer arc to center (taking into account the pullout) xco = d3.interpolateNumber(pulloutContext, sx1)(0.5); yco = d3.interpolateNumber(0, sy1)(0.5); if( (!leftHalf && sx1 < tx) || (leftHalf && sx1 > tx) ) { //If the outer point lies closer to the center than the inner point xci = tx + (tx - sx1)/2; yci = d3.interpolateNumber(ty + theight/2, sy1)(0.5); } else { xci = d3.interpolateNumber(tx, sx1)(0.25); yci = ty + theight/2; }//else context.bezierCurveTo(xco, yco, xci, yci, tx, ty + theight/2); //Draw a straight line up/down (depending on the side of the circle) context.lineTo(tx, ty - theight/2); //From center (taking into account the pullout) to start of outer arc xco = d3.interpolateNumber(pulloutContext, sx0)(0.5); yco = d3.interpolateNumber(0, sy0)(0.5); if( (!leftHalf && sx0 < tx) || (leftHalf && sx0 > tx) ) { //If the outer point lies closer to the center than the inner point xci = tx + (tx - sx0)/2; yci = d3.interpolateNumber(ty - theight/2, sy0)(0.5); } else { xci = d3.interpolateNumber(tx, sx0)(0.25); yci = ty - theight/2; }//else context.bezierCurveTo(xci, yci, xco, yco, sx0, sy0); //Close path context.closePath(); if (buffer) return context = null, buffer + "" || null; }//function string function constant$11(x) { return function() { return x; }; }//constant$11 string.radius = function(_) { return arguments.length ? (radius = typeof _ === "function" ? _ : constant$11(+_), string) : radius; }; string.startAngle = function(_) { return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$11(+_), string) : startAngle; }; string.endAngle = function(_) { return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$11(+_), string) : endAngle; }; string.x = function(_) { return arguments.length ? (x = _, string) : x; }; string.y = function(_) { return arguments.length ? (y = _, string) : y; }; string.offset = function(_) { return arguments.length ? (offset = _, string) : offset; }; string.thicknessInner = function(_) { return arguments.length ? (thicknessInner = _, string) : thicknessInner; }; string.inner = function(_) { return arguments.length ? (inner = _, string) : inner; }; string.outer = function(_) { return arguments.length ? (outer = _, string) : outer; }; string.pullout = function(_) { return arguments.length ? (pullout = _, string) : pullout; }; string.context = function(_) { return arguments.length ? ((context = _ == null ? null : _), string) : context; }; return string; }//string exports.loom = loom; exports.string = string; Object.defineProperty(exports, '__esModule', { value: true }); }));