comparison src/DML/MainVisBundle/Resources/assets/marionette/modules/GraphicsRenderingModule/GraphicsRenderingModule.20-Renderer.chord-seq.parallel-coordinates.js @ 0:493bcb69166c

added public content
author Daniel Wolff
date Tue, 09 Feb 2016 20:54:02 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:493bcb69166c
1 "use strict";
2
3 App.module("GraphicsRenderingModule", function(GraphicsRenderingModule, App, Backbone, Marionette, $, _, Logger) {
4
5 GraphicsRenderingModule.addInitializer(function(options) {
6
7 GraphicsRenderingModule.registerRenderer({
8 id: "chord-seq.parallel-coordinates",
9 inherit: "chord-seq._",
10
11 defaultVegaConfig: {
12 sizeOfStepMark: 5,
13 stepCount: 3,
14 primaryAxisFontSize: 11,
15 chordGapSizeToChordSizeRatio: 1,
16 paddingWhenAxisLabelsAreHidden: {"top": 5, "left": 15, "bottom": 10, "right": 15},
17 paddingWhenAxisLabelsAreShownParitally: {"top": 20, "left": 15, "bottom": 10, "right": 15},
18 paddingWhenAxisLabelsAreShown: {"top":NaN, "left": 15, "bottom": 10, "right": 15},
19 },
20
21 _formVC: function(vc, data) {
22 var renderer = this;
23
24 // Derive variables from the config
25 vc.numberOfRootNotes = vc.sequenceOfUsedRootNotes.length; // Excluding N (if any)
26 vc.numberOfChordTypes = vc.sequenceOfUsedChordTypes.length;
27
28 // titles of root notes
29 vc.titlesForChordTypes = vc.chordTypesWithM;
30 vc.titlesForRootNotes = vc.relativeRootNotes;
31 if (vc.recordingsInMajorModeAreIncluded && !vc.recordingsInMinorModeAreIncluded) {
32 vc.titlesForRootNotes = vc.relativeRootNotesInMajor;
33 }
34 if (vc.recordingsInMinorModeAreIncluded && !vc.recordingsInMajorModeAreIncluded) {
35 vc.titlesForRootNotes = vc.relativeRootNotesInMinor;
36 }
37
38 // calculate the sizes of the elements
39 vc.numberOfChords = vc.numberOfRootNotes * vc.numberOfChordTypes;
40 vc.numberOfGaps = vc.chordGrouppingIsByType
41 ? vc.numberOfChordTypes - 1
42 : vc.numberOfRootNotes - 1;
43
44 vc.padding = vc.paddingWhenAxisLabelsAreHidden;
45 vc.width = vc.totalWidth - vc.padding.left - vc.padding.right;
46 vc.labelsForGroupsAreShown = vc.width / (vc.numberOfGaps + 1) > 30;
47 vc.labelsForChordsAreShown = vc.width / (vc.numberOfChords + vc.numberOfGaps) > 10;
48
49 if (vc.labelsForGroupsAreShown) {
50 vc.padding = vc.paddingWhenAxisLabelsAreShownParitally;
51 }
52 if (vc.labelsForChordsAreShown) {
53 vc.padding = vc.paddingWhenAxisLabelsAreShown;
54 var titles = [];
55 if (vc.chordGrouppingIsByType) {
56 titles = _.map(vc.sequenceOfUsedRootNotes, function(index) {return vc.titlesForRootNotes[index];});
57 } else {
58 titles = _.map(vc.sequenceOfUsedChordTypes, function(index) {return vc.titlesForChordTypes[index];});
59 }
60 var maxTitleLength = 0;
61 _.each(titles, function(title) {
62 if (title.length > maxTitleLength) {
63 maxTitleLength = title.length;
64 }
65 });
66
67 vc.padding.top = maxTitleLength * 4 + 25;
68 }
69
70 if (vc.nIsIncluded) {
71 vc.numberOfChords += 1;
72 vc.numberOfGaps += 1;
73 }
74
75
76 vc.chordSize = 1;
77 do {
78 vc.chordSize++;
79 vc.chordGroupGapSize = Math.round(vc.chordSize * vc.chordGapSizeToChordSizeRatio);
80 vc.width = (vc.numberOfChords - 1) * vc.chordSize + vc.numberOfGaps * vc.chordGroupGapSize;
81 } while (vc.width + vc.padding.left + vc.padding.right < vc.totalWidth);
82
83 --vc.chordSize;
84 vc.chordGroupGapSize = Math.round(vc.chordSize * vc.chordGapSizeToChordSizeRatio);
85 vc.width = (vc.numberOfChords - 1) * vc.chordSize + vc.numberOfGaps * vc.chordGroupGapSize;
86 vc.height = vc.width + vc.padding.left + vc.padding.right - vc.padding.top - vc.padding.bottom;
87
88
89 // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
90 // Data
91
92 // .............................................................
93 // Data - links
94 var linksInVegaData = [];
95 var encodedChordSequences = data.self.stats.sequences;
96 var support = data.self.stats.support;
97
98 var sequenceId = 0;
99 _.each(encodedChordSequences, function(encodedChordSequence, i) {
100 var recordsToAdd = [];
101 var failed = false;
102 var chordSequenceHasCycles = _.unique(encodedChordSequence).length !== encodedChordSequence.length;
103 if (!vc.chordSequencesWithCyclesAreIncluded && chordSequenceHasCycles) {
104 return;
105 }
106
107 var parsedEncodedChordSequence = [];
108 var chordTitles = [];
109 _.each(encodedChordSequence, function(encodedChord, index) {
110 if (index > vc.stepCount) {
111 return;
112 }
113 var parsedEncodedChord = renderer.parseEncodedChord(encodedChord);
114 parsedEncodedChordSequence.push(parsedEncodedChord);
115 if (index == vc.stepCount) {
116 chordTitles.push("...");
117 } else {
118 chordTitles.push(renderer.titleOfParsedEncodedChord(vc, parsedEncodedChord));
119 }
120 });
121
122 var tooltip = chordTitles.join(" → ") + "<br/>support: " + support[i];
123 _.each(parsedEncodedChordSequence, function(parsedEncodedChord, indexInSequence) {
124 if (failed) {
125 return;
126 }
127 if (parsedEncodedChord !== 0 && !((vc.recordingsInMajorModeAreIncluded && parsedEncodedChord[0] == 1)
128 || (vc.recordingsInMinorModeAreIncluded && parsedEncodedChord[0] == 2))) {
129 failed = true;
130 return;
131 }
132 var bin = renderer.parsedEncodedChordToBin(vc, parsedEncodedChord);
133 if (bin === null) {
134 failed = true;
135 return;
136 }
137
138 recordsToAdd.push({
139 sequenceId: sequenceId,
140 encodedChordSequence: encodedChordSequence,
141 chordSequenceHasCycles: chordSequenceHasCycles,
142 indexInSequence: indexInSequence,
143 support: support[i],
144 chordCoordinate: renderer.chordBinToCoordinate(vc, bin),
145 color: vc.primaryColor,
146 tooltip: tooltip
147 });
148 });
149 if (!failed) {
150 linksInVegaData.push.apply(linksInVegaData, recordsToAdd);
151 }
152 ++sequenceId;
153 });
154
155 // Put most frequent on top
156 linksInVegaData.reverse();
157
158 vc.data.push({
159 "name": "links",
160 "values": linksInVegaData
161 });
162
163 // .............................................................
164 // Data - chord names in the secondary axis
165 var chordsInVegaData = [];
166 _.each(vc.sequenceOfUsedRootNotes, function(indexOfRootNote) {
167 _.each(vc.sequenceOfUsedChordTypes, function(indexOfChordType) {
168 var bin = renderer.parsedEncodedChordToBin(vc, [0, indexOfRootNote, indexOfChordType]);
169 chordsInVegaData.push({
170 rootNoteIndex: indexOfRootNote,
171 rootNoteTitle: vc.titlesForRootNotes[indexOfRootNote],
172
173 chordTypeIndex: indexOfChordType,
174 chordTypeTitle: vc.titlesForChordTypes[indexOfChordType],
175
176 chordCoordinate: renderer.chordBinToCoordinate(vc, bin)
177 });
178 });
179 });
180 vc.data.push({
181 "name": "chords",
182 "values": chordsInVegaData,
183 });
184
185 // .............................................................
186 // Data - chord names in the primary axis
187 if (vc.labelsForGroupsAreShown) {
188 var groupsForVega = [];
189 if (vc.chordGrouppingIsByType) {
190 groupsForVega = chordsInVegaData.filter(function(chord) {
191 return chord.rootNoteIndex == vc.sequenceOfUsedRootNotes[0];
192 });
193 } else {
194 groupsForVega = chordsInVegaData.filter(function(chord) {
195 return chord.chordTypeIndex == vc.sequenceOfUsedChordTypes[0];
196 });
197 }
198 vc.data.push({
199 "name": "groups",
200 "values": groupsForVega,
201 });
202 }
203
204 // .............................................................
205 // Data - steps
206 var stepsInVegaData = _.range(0, vc.stepCount);
207 vc.data.push({
208 "name": "steps",
209 "values": stepsInVegaData,
210 });
211
212
213 // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
214 // Scales
215
216 // .............................................................
217 // Scale - steps
218 vc.scales.push({
219 //"type": "ordinal",
220 "name": "indexInSequence",
221 "domainMin": 0,
222 "domainMax": vc.stepCount - 1,
223 "point": true,
224 "round": true,
225 "range": [0, vc.height]
226 });
227
228 // .............................................................
229 // Scale - line opacity
230 vc.scales.push({
231 "name": "strokeOpacity",
232 "type": "linear",
233 "domain": [0, data.self.coverage["ok_count"] * 2],
234 "point": true,
235 "range": [0, 1]
236 });
237
238
239 // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
240 // Marks
241
242 // .............................................................
243 // Mark - vertical lines for chords
244 if (true || vc.guidesAreVisible) {
245 vc.marks.push({
246 "type": "rect",
247 "from": {"data": "chords"},
248 "properties": {
249 "enter": {
250 "x": {"field": "chordCoordinate"},
251 "y": {"value": 0},
252 "width": {"value": 1},
253 "height": {"field": {"group": "height"}},
254 "fill": {"value": vc.colorForAxes}
255 },
256 }
257 });
258 }
259
260 // .............................................................
261 // Mark - vertical lines for N chord
262 if (vc.nIsIncluded && vc.guidesAreVisible) {
263 vc.marks.push({
264 "type": "rect",
265 "properties": {
266 "enter": {
267 "x": {"field": {"group": "width"}},
268 "y": {"value": 0},
269 "width": {"value": "1"},
270 "height": {"field": {"group": "height"}},
271 "fill": {"value": vc.colorForAxes},
272 },
273 }
274 });
275 }
276
277 // .............................................................
278 // Mark - horisontal lines denoting steps
279 if (vc.guidesAreVisible) {
280 for (var right = 0; right < 2; right++) {
281 vc.marks.push({
282 "type": "rect",
283 "from": {"data": "steps"},
284 "properties": {
285 "enter": {
286 "x": right ? {"field": {"group": "width"}} : {"value": -vc.sizeOfStepMark},
287 "width": {"value": vc.sizeOfStepMark},
288 "y": {"scale": "indexInSequence", "field": "data"},
289 "height": {"value": 1},
290 "fill": {"value": vc.colorForAxes},
291 }
292 }
293 });
294 }
295 }
296
297 // .............................................................
298 // Mark - links
299 vc.marks.push({
300 "type": "group",
301 "from": {
302 "data": "links",
303 "transform": [ {
304 "type": "facet",
305 "groupby": ["sequenceId"]
306 } ]
307 },
308 "marks": [{
309 "type": "line",
310 "properties": {
311 "enter": {
312 "x": {"field": "chordCoordinate"},
313 "y": {"scale": "indexInSequence", "field": "indexInSequence"},
314 "strokeOpacity": {"scale": "strokeOpacity", "field": "support"},
315 "stroke": {"field": "color"},
316 "strokeWidth": {"value": 2},
317 },
318 "update": {
319 "strokeOpacity": {"scale": "strokeOpacity", "field": "support"},
320 "stroke": {"field": "color"},
321 },
322 "hover": {
323 "strokeOpacity": {"value": 1},
324 "stroke": {"value": "#000"},
325 }
326 },
327 }]
328 });
329
330 // .............................................................
331 // Mark - group name (primary axis)
332 vc.yOffsetForGroupLabels = -vc.padding.top + 15;
333 vc.yOffsetForChordLabels = -3;
334
335 vc.xOffsetForGroupLabels = 0.5 * vc.chordSize * ((vc.chordGrouppingIsByType ? vc.numberOfRootNotes : vc.numberOfChordTypes) - 1);
336
337 if (vc.labelsForGroupsAreShown) {
338 vc.marks.push({
339 "type": "text",
340 "from": {"data": "groups"},
341 "properties": {
342 "enter": {
343 "x": {"field": "chordCoordinate", "offset": vc.xOffsetForGroupLabels},
344 "y": {"value": vc.yOffsetForGroupLabels},
345 "text": {"field": vc.chordGrouppingIsByType ? "chordTypeTitle": "rootNoteTitle"},
346 "baseline": {"value":"bottom"},
347 "align": {"value": "center"},
348 "fill": {"value": vc.colorForAxisLabels},
349 "font": {"value": vc.fontFace},
350 "fontSize": {"value": vc.fontSizeForLabelsInAxis},
351 },
352 }
353 });
354 }
355
356 // .............................................................
357 // Mark - column name for N
358 if (vc.nIsIncluded && vc.labelsForGroupsAreShown) {
359 vc.marks.push({
360 "type": "text",
361 "properties": {
362 "enter": {
363 "y": {"value": vc.labelsForChordsAreShown ? vc.yOffsetForChordLabels : vc.yOffsetForGroupLabels},
364 "x": {"field": {"group": "width"}},
365 "text": {"value":"N"},
366 "align": {"value":"center"},
367 "baseline": {"value":"bottom"},
368 "fill": {"value": vc.colorForAxisLabels},
369 "font": {"value": vc.fontFace},
370 "fontSize": {"value": vc.fontSizeForLabelsInAxis},
371 },
372 }
373 });
374 }
375
376 // .............................................................
377 // Mark - chord name (secondary axis)
378 if (vc.labelsForChordsAreShown) {
379 vc.marks.push({
380 "type": "text",
381 "from": {"data": "chords"},
382 "properties": {
383 "enter": {
384 "x": {"field": "data.chordCoordinate", "offset": 0},
385 "y": {"value": vc.yOffsetForChordLabels},
386 "text": {"field": vc.chordGrouppingIsByType ? "data.rootNoteTitle": "data.chordTypeTitle"},
387 "angle": {"value": "-90"},
388 "baseline": {"value": "middle"},
389 "fill": {"value": vc.colorForAxisLabels},
390 "font": {"value": vc.fontFace},
391 "fontSize": {"value": vc.fontSizeForLabelsInSecondaryAxis},
392 },
393 }
394 });
395 }
396
397 // .............................................................
398 // Mark - fader
399 vc.marks.push({
400 "type": "image",
401 "from": {"data": "dummy"},
402 "properties": {
403 "enter": {
404 "x": {"value": 0},
405 "width": {"field": {"group": "width"}},
406 "y": {"field": {"group": "height"}},
407 "height": {"value": vc.padding.bottom + 2},
408 "url": {"value": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQIAAAAMCAYAAACJMrOUAAABFUlEQVRoge2PwW0CQQxFXQEUAoVAYaGT0Ek64Lq5QgMgLVJ4uezBspYsoxnCsPMPT1/y2B4/A5aOhctFqD3ynkvq/tL3yF/+TfobsAZWA2uXj7AK85G4L/bn/pc6X3qf/OU/C38DtglsXG5CrUZK3yt/+c/S34BdBh8ux/A9Y/1T77n7no385T8LfwM+/2Dvch9qJfpT53P3pyJ/+Tfhb8CXEKJtDDgIIdrGgA74vkPnsgu1V5B7T+q8/OXfhL8Bp3/k6PIYai0gf/lX6W/AGbgMnF2OcQn9kTgf+1Pfp/6f2pd7n/zl34S/AT1wHehd9qE29h6Z6s/dF6n9XvnL/y38Dfhx3FzeQq0Ez95f+33yl3+V/r9d8S8/zvVSHwAAAABJRU5ErkJggg=="},
409 "fill": {"value": "#F00"},
410 },
411 }
412 });
413 }
414 });
415 });
416 }, Logger);