Пользовательская кривая D3: интерполяция для областей - PullRequest
0 голосов
/ 26 мая 2018

Рассмотрим этот график D3JS, который использует базис интерполяцию:

D3JS v3, basis interpolation

В D3JS v3 я мог бы использовать вместо этого * интерполировать (.interpolate("bundle").tension(0)) областей для достижения этого типа рендеринга:

D3JS v3, bundle interpolation

Обратите внимание, как хорошо подходит каждый сегмент графасо своими соседями.Это то, что мне нужно.

С D3JS v4 и v5 синтаксис для интерполяции пакетов теперь такой: .curve(d3.curveBundle).Однако теперь он "предназначен для работы с d3.line, а не с d3.area."

Я недавно обновился с v3 до v5, и поэтому я пытаюсь создатьпользовательская кривая связки, которая будет работать и с областями , чтобы сохранить тип интерполяции, который мне понравился с v3.

Я очень близок.Это то, что я до сих пор:

///////////////////// Custom curves.

/** Bundle-ish.
 * Trying to adapt curveBundle for use with areas…
 */
function myBundle(context, beta) {
	this._basis = new d3.curveBasis(context);
	this._beta = beta;

	this._context = context; // temporary. shouldn't be needed for bundle.
}
myBundle.prototype = {

	areaStart: function() {
		this._line = 0;
	},
	areaEnd: function() {
		this._line = NaN;
	},
	lineStart: function() {
		this._x = [];
		this._y = [];
		this._basis.lineStart();
	},
	lineEnd: function() {
		var x = this._x,
				y = this._y,
				j = x.length - 1;

		if (j > 0) {
			var x0 = x[0],
					y0 = y[0],
					dx = x[j] - x0,
					dy = y[j] - y0,
					i = -1,
					t;

			while (++i <= j) {
				t = i / j;
				this._basis.point(
					this._beta * x[i] + (1 - this._beta) * (x0 + t * dx),
					this._beta * y[i] + (1 - this._beta) * (y0 + t * dy)
				);
			}
		}

		this._x = this._y = null;
		this._basis.lineEnd();
	},
	point: function(x, y) {
		this._x.push(+x);
		this._y.push(+y);
		// console.log( this._x.push(+x), this._y.push(+y) );
	}
};
myCurveBundle = (function custom(beta) {
	function myCurveBundle(context) {
		return beta === 1 ? new myBasis(context) : new myBundle(context, beta);
	}

	myCurveBundle.beta = function(beta) {
		return custom(+beta);
	};

	return myCurveBundle;
})(0.85);






///////////////////// The chart.

	var width = 960;
	var height = 540;
	var data = [];
	data.prosody = [116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.578, 125.552, 134.888, 144.225, 153.561, 162.898, 172.235, 181.571, 190.908, 200.244, 209.581, 218.917, 227.715, 218.849, 209.591, 200.333, 191.076, 181.818, 172.560, 163.302, 154.044, 144.787, 135.529, 126.271, 117.013, 107.755, 98.498, 89.240, 97.511, 118.857, 140.202, 161.547, 182.893, 192.100, 188.997, 185.895, 182.792, 179.690, 176.587, 173.485, 170.382, 167.280, 164.177, 161.075, 157.972, 154.870, 151.767, 148.665, 145.562, 142.460, 139.357, 136.255, 133.152, 130.050, 126.947, 124.244, 122.275, 120.307, 118.338, 116.369, 114.400, 112.431, 110.462, 108.493, 106.524, 104.555, 102.586, 100.617, 98.648, 99.659, 101.531, 103.402, 105.273, 107.145, 109.016, 110.887, 112.758, 114.630, 116.501, 118.372, 120.244, 122.115, 123.986, 125.857, 127.729, 129.600, 131.471, 133.343, 135.214, 137.085, 138.956, 140.828, 142.699, 144.570, 146.442, 148.313, 150.184, 149.175, 146.384, 143.594, 140.803, 138.013, 135.222, 132.432, 129.642, 126.851, 124.061, 121.270, 118.480, 115.689, 112.899, 110.109, 107.318, 104.528, 101.737, 98.947, 96.156, 93.366, 90.576, 87.785, 84.995, 82.204, 79.414, 76.623, 0, 0, 0, 0, 0, 0, 76.601, 78.414, 80.227, 82.041, 83.854, 85.667, 87.480, 89.294, 91.107, 92.920, 94.733, 96.547, 98.360, 100.173, 101.986, 103.800, 105.613, 107.426, 109.239, 111.053, 112.866, 114.679, 116.492, 115.917, 114.338, 112.760, 111.181, 109.602, 108.023, 106.444, 104.865, 103.286, 101.707, 100.128, 98.549, 96.970, 95.391, 93.812, 92.233, 90.654, 89.075, 87.534, 88.055, 88.646, 89.237, 89.827, 90.418, 91.009, 91.600, 92.191, 92.782, 93.373, 93.964, 94.555, 95.146, 95.737, 96.328, 96.919, 97.509, 98.100, 98.691, 99.282, 99.873, 100.062, 98.230, 96.399, 94.567, 92.736, 90.904, 89.072, 87.241, 85.409, 83.578, 81.746, 79.914, 78.083, 78.839, 80.880, 82.922, 84.964, 87.006, 89.048, 91.090, 93.132, 95.174, 97.216, 99.257, 101.299, 103.341, 105.383, 107.425, 109.467, 111.509, 113.551, 112.633, 110.755, 108.877, 106.999, 105.121, 103.243, 101.365, 99.487, 97.609, 95.731, 93.853, 91.975, 90.097, 88.219, 86.341, 84.463, 82.585, 80.707, 78.829, 76.951, 78.067, 81.290, 84.513, 87.736, 90.958, 94.181, 97.404, 100.627, 103.849, 107.072, 110.295, 113.517, 116.740, 119.963, 123.186, 126.408, 129.631, 132.854, 136.077, 139.299, 142.522, 145.745, 148.968, 152.190, 155.413, 154.840, 152.899, 150.958, 149.017, 147.076, 145.135, 143.194, 141.253, 139.312, 137.371, 135.429, 133.488, 131.547, 129.606, 127.665, 125.724, 124.874, 126.734, 128.594, 130.454, 132.314, 134.174, 136.034, 137.894, 139.754, 141.614, 143.474, 145.334, 147.194, 149.054, 150.914, 152.774, 154.634, 156.494, 158.354, 160.214, 162.074, 163.934, 165.664, 161.795, 157.761, 153.726, 149.692, 145.658, 141.624, 137.589, 133.555, 129.521, 125.487, 121.452, 117.418, 113.384, 109.350, 105.316, 101.281, 97.247, 93.213, 89.179, 85.144, 81.110, 77.076, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
	data.TextGrid = {  "phone" : [ /** segment type, beginning, and end of each segment **/ [ "sp", 0.0124716553288, 0.271882086168 ], [ "M", 0.271882086168, 0.401587301587 ], [ "OW", 0.401587301587, 0.521315192744 ], [ "S", 0.521315192744, 0.660997732426 ], [ "T", 0.660997732426, 0.710884353741 ], [ "AH", 0.710884353741, 0.760770975057 ], [ "V", 0.760770975057, 0.820634920635 ], [ "DH", 0.820634920635, 0.860544217687 ], [ "IY", 0.860544217687, 0.940362811791 ], [ "AH", 0.940362811791, 0.980272108844 ], [ "D", 0.980272108844, 1.04013605442 ], [ "V", 1.04013605442, 1.10997732426 ], [ "EH", 1.10997732426, 1.21972789116 ], [ "N", 1.21972789116, 1.289569161 ], [ "CH", 1.289569161, 1.42925170068 ], [ "ER", 1.42925170068, 1.51904761905 ], [ "Z", 1.51904761905, 1.57891156463 ], [ "R", 1.57891156463, 1.66870748299 ], [ "AH", 1.66870748299, 1.69863945578 ], [ "K", 1.69863945578, 1.75850340136 ], [ "AO", 1.75850340136, 1.88820861678 ], [ "R", 1.88820861678, 1.91814058957 ], [ "D", 1.91814058957, 1.95804988662 ], [ "AH", 1.95804988662, 1.99795918367 ], [ "D", 1.99795918367, 2.07777777778 ], [ "AH", 2.07777777778, 2.10770975057 ], [ "N", 2.10770975057, 2.18752834467 ], [ "DH", 2.18752834467, 2.22743764172 ], [ "AH", 2.22743764172, 2.2873015873 ], [ "S", 2.2873015873, 2.42698412698 ], [ "B", 2.42698412698, 2.51678004535 ], [ "UH", 2.51678004535, 2.68639455782 ], [ "K", 2.68639455782, 2.79614512472 ], [ "sp", 2.79614512472, 2.81609977324 ], [ "R", 2.81609977324, 2.95578231293 ], [ "IY", 2.95578231293, 3.00566893424 ], [ "L", 3.00566893424, 3.09546485261 ], [ "IY", 3.09546485261, 3.23514739229 ], [ "AH", 3.23514739229, 3.27505668934 ], [ "K", 3.27505668934, 3.41473922902 ], [ "ER", 3.41473922902, 3.68412698413 ], [ "D", 3.68412698413, 3.75396825397 ], [ "sp", 3.75396825397, 4.01337868481 ] ] }


	/**
	 * Set up D3JS
	 */
	var x = d3.scaleLinear()
		.domain([0, 401])
		.range([0, width]);
	var y = d3.scaleLinear()
		.domain([0, 800])
		.range([height, 0]);

	/** Center the stream vertically **/
	var shift = d3.scaleLinear()
		.domain([0, 0])
		.range([-height/2, 0]);

	/** Draw a stream segment **/
	var pathGenerator = d3.area()
		.curve( myCurveBundle.beta(0) )
		.x(function(d, i) { return x(i); })
		.y1(function(d) { return y(d + 72 ); }) /** 72 is just some arbitrary thickess given to the graph **/
		.y0(function(d) { return y(d); });

	var svg = d3.select("body").append("svg")
		.attr("width", width)
		.attr("height", height);



	/**
	 * Render the chart
	 */
	
	/** Draw the stream, on a per-segment basis **/
	var path = svg.selectAll("path")
		.data(data.TextGrid.phone)
		.enter().append("path")
			.attr("transform", function(d, i) { return "translate(" + x(Math.floor(d[1]*100)) + ", " + shift(i) + ")"; })
			.attr("class", function(d) { return "segment " + d[0]; })
			.on('click', function(d, i) { playFromTo(Math.floor(d[1] * 1000), Math.floor(d[2] * 1000)); })
			.attr("d", function(d) { return pathGenerator(data.prosody.slice( Math.floor(d[1]*100), Math.floor(d[2]*100)+1)); });
.segment { fill: #ccc; }
.segment.sp { display: none; }

/** Adapted from Margaret Horrigan for American English **/
.segment.IY { fill: #7AC141; }
.segment.IH { fill: #F9C5DC; }
.segment.UH { fill: #FF00FF; }
.segment.UW { fill: #0153A5; }
.segment.EY { fill: #8B8C90; }
.segment.EH { fill: #E61A25; }
.segment.AX { fill: #DF5435; }
.segment.ER { fill: #805EAA; }
.segment.AO { fill: #E2A856; }
.segment.OY { fill: #2E3094; }
.segment.OW { fill: #FC2B1C; }
.segment.AE { fill: #21201E; }
.segment.AH { fill: #DF5435; }
.segment.AA { fill: #bf181f; }
.segment.AY { fill: #FFFFFF; }
.segment.AW { fill: #7C4540; }
<script src="https://cdn.jsdelivr.net/npm/d3@5.4.0/dist/d3.min.js"></script>

(Код комплекта адаптирован из bundle.js в d3-shape .)

Я очень близко: если вы осмотрите SVG, вы увидите, что, хотя ничего не показано, пути действительно создаются.

Если вы посмотритев первом «видимом» сегменте (класс segment M) вы увидите, что он содержит команду move где-то посередине:

M31.122194513715712,398.532825

Если я переименую его в строкукоманда, например, так:

L31.122194513715712,398.532825

… тогда этот сегмент покажет.

Я не понимаю, какая часть пользовательской кривой отвечает за это. Как я могу превратить это M в L?

Получающиеся пути также не имеют окончательных Z.Как бы я справился с этим?

Я не нашел большой помощи относительно пользовательских кривых в D3JS.Любая помощь приветствуется.

1 Ответ

0 голосов
/ 29 мая 2018

Вызов areaStart и areaEnd:

Похоже, вы скопировали функции areaStart и areaEnd из d3.curveBasis в свою собственную кривую, которая в основном рисуется из d3.curveBundle.

Вы не можете просто скопировать код из d3.curveBasis в d3.curveBundle, потому что this относится к разным вещам.this._basis (ваш экземпляр d3.curveBasis) не может получить доступ к this._line (переменная, которую он ожидает для areaStart и areaEnd, и доступна через ваши customBundle).

Вы можете изменитьthis._line до this._basis._line, но, если вы заметили, все функции линии в d3.curveBundle вызывают свои соответствующие this._basis функции (например, lineStart вызывает this._basis.lineStart()).Если вы сделаете то же самое здесь, это должно быть эквивалентно:

areaStart: {
    // this._basis._line = 0; // this should work, for now
    this._basis.areaStart(); // but this makes more sense semantically
},
areaEnd: {
    // this._basis._line = NaN; // this should work, for now
    this._basis.areaEnd(); // but this makes more sense semantically
}

Дополнительное преимущество таких действий состоит в том, что если d3.curveBasis изменит свою реализацию в будущем, у этого будет больше шансов на совместимость.

Нет необходимости в new:

В качестве примечания, в конструкторе вы создаете новый экземпляр this._basis, используя оператор new:

this._basis = new d3.curveBasis(context);

Конструктор Basis через new используется внутри модулей d3, но в связанной библиотеке это функциональный конструктор.Это может быть просто:

this._basis = d3.curveBasis(context);

Хотя использование new, похоже, ничего не нарушает.См. https://stackoverflow.com/a/9468106/6184972 для получения дополнительной информации.

Если вы используете кривые связки для визуализации областей?

Как вы заметили, d3.curveBundle "предназначен для работы с d3.line,не d3.area. "Стоит спросить, следует ли вам использовать curveBundle для рендеринга областей, поскольку упущение кажется преднамеренным.От https://github.com/d3/d3-shape/issues/70, @mbostock пишет:

Правильно, d3.curveBundle предназначен только для работы с d3.line.Это для иерархического связывания ребер, а не для рендеринга областей.

См. Также https://github.com/d3/d3-shape#curveBundle.

Вы, вероятно, должны сравнить с curveBundle с другими методами интерполяции, чтобы увидеть, не искажает ли его использованиевводящая в заблуждение область.

Результаты:

Все вместе, изменения можно увидеть в этой скрипке: https://jsfiddle.net/g4ya2qso/

Альтернативно:

Альтернативнопоскольку функциональность очень похожа на d3.curveBundle, вы можете просто добавить методы для .areaStart и .areaEnd и опустить весь другой пользовательский код, например:

var myCurveBundle = (function custom(beta) {
    function myCustomBundle(context) {
      var bundle = d3.curveBundle.beta(beta)(context);
      bundle.areaStart = function () {
        bundle._basis.areaStart();
      };
      bundle.areaEnd = function () {
        bundle._basis.areaEnd();
      };
      return bundle;
    }
    myCustomBundle.beta = function(newBeta) {
      return custom(+newBeta);
    };
    return myCustomBundle;
})(0.85);

///////////////////// Custom curves.

/** Bundle-ish.
 * Trying to adapt curveBundle for use with areas…
 */
var myCurveBundle = (function custom(beta) {
  function myCustomBundle(context) {
    var bundle = d3.curveBundle.beta(beta)(context);
    bundle.areaStart = function () {
      bundle._basis.areaStart();
    };
    bundle.areaEnd = function () {
      bundle._basis.areaEnd();
    };
    return bundle;
  }
  myCustomBundle.beta = function(newBeta) {
    return custom(+newBeta);
  };
  return myCustomBundle;
})(0.85);

///////////////////// The chart.

	var width = 960;
	var height = 540;
	var data = [];
	data.prosody = [116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.473, 116.578, 125.552, 134.888, 144.225, 153.561, 162.898, 172.235, 181.571, 190.908, 200.244, 209.581, 218.917, 227.715, 218.849, 209.591, 200.333, 191.076, 181.818, 172.560, 163.302, 154.044, 144.787, 135.529, 126.271, 117.013, 107.755, 98.498, 89.240, 97.511, 118.857, 140.202, 161.547, 182.893, 192.100, 188.997, 185.895, 182.792, 179.690, 176.587, 173.485, 170.382, 167.280, 164.177, 161.075, 157.972, 154.870, 151.767, 148.665, 145.562, 142.460, 139.357, 136.255, 133.152, 130.050, 126.947, 124.244, 122.275, 120.307, 118.338, 116.369, 114.400, 112.431, 110.462, 108.493, 106.524, 104.555, 102.586, 100.617, 98.648, 99.659, 101.531, 103.402, 105.273, 107.145, 109.016, 110.887, 112.758, 114.630, 116.501, 118.372, 120.244, 122.115, 123.986, 125.857, 127.729, 129.600, 131.471, 133.343, 135.214, 137.085, 138.956, 140.828, 142.699, 144.570, 146.442, 148.313, 150.184, 149.175, 146.384, 143.594, 140.803, 138.013, 135.222, 132.432, 129.642, 126.851, 124.061, 121.270, 118.480, 115.689, 112.899, 110.109, 107.318, 104.528, 101.737, 98.947, 96.156, 93.366, 90.576, 87.785, 84.995, 82.204, 79.414, 76.623, 0, 0, 0, 0, 0, 0, 76.601, 78.414, 80.227, 82.041, 83.854, 85.667, 87.480, 89.294, 91.107, 92.920, 94.733, 96.547, 98.360, 100.173, 101.986, 103.800, 105.613, 107.426, 109.239, 111.053, 112.866, 114.679, 116.492, 115.917, 114.338, 112.760, 111.181, 109.602, 108.023, 106.444, 104.865, 103.286, 101.707, 100.128, 98.549, 96.970, 95.391, 93.812, 92.233, 90.654, 89.075, 87.534, 88.055, 88.646, 89.237, 89.827, 90.418, 91.009, 91.600, 92.191, 92.782, 93.373, 93.964, 94.555, 95.146, 95.737, 96.328, 96.919, 97.509, 98.100, 98.691, 99.282, 99.873, 100.062, 98.230, 96.399, 94.567, 92.736, 90.904, 89.072, 87.241, 85.409, 83.578, 81.746, 79.914, 78.083, 78.839, 80.880, 82.922, 84.964, 87.006, 89.048, 91.090, 93.132, 95.174, 97.216, 99.257, 101.299, 103.341, 105.383, 107.425, 109.467, 111.509, 113.551, 112.633, 110.755, 108.877, 106.999, 105.121, 103.243, 101.365, 99.487, 97.609, 95.731, 93.853, 91.975, 90.097, 88.219, 86.341, 84.463, 82.585, 80.707, 78.829, 76.951, 78.067, 81.290, 84.513, 87.736, 90.958, 94.181, 97.404, 100.627, 103.849, 107.072, 110.295, 113.517, 116.740, 119.963, 123.186, 126.408, 129.631, 132.854, 136.077, 139.299, 142.522, 145.745, 148.968, 152.190, 155.413, 154.840, 152.899, 150.958, 149.017, 147.076, 145.135, 143.194, 141.253, 139.312, 137.371, 135.429, 133.488, 131.547, 129.606, 127.665, 125.724, 124.874, 126.734, 128.594, 130.454, 132.314, 134.174, 136.034, 137.894, 139.754, 141.614, 143.474, 145.334, 147.194, 149.054, 150.914, 152.774, 154.634, 156.494, 158.354, 160.214, 162.074, 163.934, 165.664, 161.795, 157.761, 153.726, 149.692, 145.658, 141.624, 137.589, 133.555, 129.521, 125.487, 121.452, 117.418, 113.384, 109.350, 105.316, 101.281, 97.247, 93.213, 89.179, 85.144, 81.110, 77.076, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
	data.TextGrid = {  "phone" : [ /** segment type, beginning, and end of each segment **/ [ "sp", 0.0124716553288, 0.271882086168 ], [ "M", 0.271882086168, 0.401587301587 ], [ "OW", 0.401587301587, 0.521315192744 ], [ "S", 0.521315192744, 0.660997732426 ], [ "T", 0.660997732426, 0.710884353741 ], [ "AH", 0.710884353741, 0.760770975057 ], [ "V", 0.760770975057, 0.820634920635 ], [ "DH", 0.820634920635, 0.860544217687 ], [ "IY", 0.860544217687, 0.940362811791 ], [ "AH", 0.940362811791, 0.980272108844 ], [ "D", 0.980272108844, 1.04013605442 ], [ "V", 1.04013605442, 1.10997732426 ], [ "EH", 1.10997732426, 1.21972789116 ], [ "N", 1.21972789116, 1.289569161 ], [ "CH", 1.289569161, 1.42925170068 ], [ "ER", 1.42925170068, 1.51904761905 ], [ "Z", 1.51904761905, 1.57891156463 ], [ "R", 1.57891156463, 1.66870748299 ], [ "AH", 1.66870748299, 1.69863945578 ], [ "K", 1.69863945578, 1.75850340136 ], [ "AO", 1.75850340136, 1.88820861678 ], [ "R", 1.88820861678, 1.91814058957 ], [ "D", 1.91814058957, 1.95804988662 ], [ "AH", 1.95804988662, 1.99795918367 ], [ "D", 1.99795918367, 2.07777777778 ], [ "AH", 2.07777777778, 2.10770975057 ], [ "N", 2.10770975057, 2.18752834467 ], [ "DH", 2.18752834467, 2.22743764172 ], [ "AH", 2.22743764172, 2.2873015873 ], [ "S", 2.2873015873, 2.42698412698 ], [ "B", 2.42698412698, 2.51678004535 ], [ "UH", 2.51678004535, 2.68639455782 ], [ "K", 2.68639455782, 2.79614512472 ], [ "sp", 2.79614512472, 2.81609977324 ], [ "R", 2.81609977324, 2.95578231293 ], [ "IY", 2.95578231293, 3.00566893424 ], [ "L", 3.00566893424, 3.09546485261 ], [ "IY", 3.09546485261, 3.23514739229 ], [ "AH", 3.23514739229, 3.27505668934 ], [ "K", 3.27505668934, 3.41473922902 ], [ "ER", 3.41473922902, 3.68412698413 ], [ "D", 3.68412698413, 3.75396825397 ], [ "sp", 3.75396825397, 4.01337868481 ] ] }

	/**
	 * Set up D3JS
	 */
	var x = d3.scaleLinear()
		.domain([0, 401])
		.range([0, width]);
	var y = d3.scaleLinear()
		.domain([0, 800])
		.range([height, 0]);

	/** Center the stream vertically **/
	var shift = d3.scaleLinear()
		.domain([0, 0])
		.range([-height/2, 0]);

	/** Draw a stream segment **/
	var pathGenerator = d3.area()
		.curve( myCurveBundle.beta(0) )
		.x(function(d, i) { return x(i); })
		.y1(function(d) { return y(d + 72 ); }) /** 72 is just some arbitrary thickess given to the graph **/
		.y0(function(d) { return y(d); });

	var svg = d3.select("body").append("svg")
		.attr("width", width)
		.attr("height", height);

	/**
	 * Render the chart
	 */
	
	/** Draw the stream, on a per-segment basis **/
	var path = svg.selectAll("path")
		.data(data.TextGrid.phone)
		.enter().append("path")
			.attr("transform", function(d, i) { return "translate(" + x(Math.floor(d[1]*100)) + ", " + shift(i) + ")"; })
			.attr("class", function(d) { return "segment " + d[0]; })
			.on('click', function(d, i) { playFromTo(Math.floor(d[1] * 1000), Math.floor(d[2] * 1000)); })
			.attr("d", function(d) { return pathGenerator(data.prosody.slice( Math.floor(d[1]*100), Math.floor(d[2]*100)+1)); });
.segment { fill: #ccc; }
.segment.sp { display: none; }

/** Adapted from Margaret Horrigan for American English **/
.segment.IY { fill: #7AC141; }
.segment.IH { fill: #F9C5DC; }
.segment.UH { fill: #FF00FF; }
.segment.UW { fill: #0153A5; }
.segment.EY { fill: #8B8C90; }
.segment.EH { fill: #E61A25; }
.segment.AX { fill: #DF5435; }
.segment.ER { fill: #805EAA; }
.segment.AO { fill: #E2A856; }
.segment.OY { fill: #2E3094; }
.segment.OW { fill: #FC2B1C; }
.segment.AE { fill: #21201E; }
.segment.AH { fill: #DF5435; }
.segment.AA { fill: #bf181f; }
.segment.AY { fill: #FFFFFF; }
.segment.AW { fill: #7C4540; }
<script src="https://cdn.jsdelivr.net/npm/d3@5.4.0/dist/d3.min.js"></script>
...