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