diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/DML/MainVisBundle/Resources/assets/marionette/modules/GraphicsRenderingModule/GraphicsRenderingModule.20-Renderer.chord-seq.parallel-coordinates.js	Tue Feb 09 20:54:02 2016 +0100
@@ -0,0 +1,416 @@
+"use strict";
+
+App.module("GraphicsRenderingModule", function(GraphicsRenderingModule, App, Backbone, Marionette, $, _, Logger) {
+
+    GraphicsRenderingModule.addInitializer(function(options) {
+
+        GraphicsRenderingModule.registerRenderer({
+            id: "chord-seq.parallel-coordinates",
+            inherit: "chord-seq._",
+
+            defaultVegaConfig: {
+                sizeOfStepMark: 5,
+                stepCount: 3,
+                primaryAxisFontSize: 11,
+                chordGapSizeToChordSizeRatio: 1,
+                paddingWhenAxisLabelsAreHidden:         {"top":  5, "left": 15, "bottom": 10, "right": 15},
+                paddingWhenAxisLabelsAreShownParitally: {"top": 20, "left": 15, "bottom": 10, "right": 15},
+                paddingWhenAxisLabelsAreShown:          {"top":NaN, "left": 15, "bottom": 10, "right": 15},
+            },
+
+            _formVC: function(vc, data) {
+                var renderer = this;
+
+                // Derive variables from the config
+                vc.numberOfRootNotes = vc.sequenceOfUsedRootNotes.length; // Excluding N (if any)
+                vc.numberOfChordTypes = vc.sequenceOfUsedChordTypes.length;
+
+                // titles of root notes
+                vc.titlesForChordTypes = vc.chordTypesWithM;
+                vc.titlesForRootNotes = vc.relativeRootNotes;
+                if (vc.recordingsInMajorModeAreIncluded && !vc.recordingsInMinorModeAreIncluded) {
+                    vc.titlesForRootNotes = vc.relativeRootNotesInMajor;
+                }
+                if (vc.recordingsInMinorModeAreIncluded && !vc.recordingsInMajorModeAreIncluded) {
+                    vc.titlesForRootNotes = vc.relativeRootNotesInMinor;
+                }
+
+                // calculate the sizes of the elements
+                vc.numberOfChords = vc.numberOfRootNotes * vc.numberOfChordTypes;
+                vc.numberOfGaps   = vc.chordGrouppingIsByType
+                        ? vc.numberOfChordTypes - 1
+                        : vc.numberOfRootNotes - 1;
+
+                vc.padding = vc.paddingWhenAxisLabelsAreHidden;
+                vc.width = vc.totalWidth - vc.padding.left - vc.padding.right;
+                vc.labelsForGroupsAreShown = vc.width / (vc.numberOfGaps + 1) > 30;
+                vc.labelsForChordsAreShown = vc.width / (vc.numberOfChords + vc.numberOfGaps) > 10;
+
+                if (vc.labelsForGroupsAreShown) {
+                    vc.padding = vc.paddingWhenAxisLabelsAreShownParitally;
+                }
+                if (vc.labelsForChordsAreShown) {
+                    vc.padding = vc.paddingWhenAxisLabelsAreShown;
+                    var titles = [];
+                    if (vc.chordGrouppingIsByType) {
+                        titles = _.map(vc.sequenceOfUsedRootNotes, function(index) {return vc.titlesForRootNotes[index];});
+                    } else {
+                        titles = _.map(vc.sequenceOfUsedChordTypes, function(index) {return vc.titlesForChordTypes[index];});
+                    }
+                    var maxTitleLength = 0;
+                    _.each(titles, function(title) {
+                        if (title.length > maxTitleLength) {
+                            maxTitleLength = title.length;
+                        }
+                    });
+
+                    vc.padding.top = maxTitleLength * 4 + 25;
+                }
+
+                if (vc.nIsIncluded) {
+                    vc.numberOfChords += 1;
+                    vc.numberOfGaps   += 1;
+                }
+
+
+                vc.chordSize = 1;
+                do {
+                    vc.chordSize++;
+                    vc.chordGroupGapSize = Math.round(vc.chordSize * vc.chordGapSizeToChordSizeRatio);
+                    vc.width = (vc.numberOfChords - 1) * vc.chordSize + vc.numberOfGaps * vc.chordGroupGapSize;
+                } while (vc.width + vc.padding.left + vc.padding.right < vc.totalWidth);
+
+                --vc.chordSize;
+                vc.chordGroupGapSize = Math.round(vc.chordSize * vc.chordGapSizeToChordSizeRatio);
+                vc.width = (vc.numberOfChords - 1) * vc.chordSize + vc.numberOfGaps * vc.chordGroupGapSize;
+                vc.height = vc.width + vc.padding.left + vc.padding.right - vc.padding.top -  vc.padding.bottom;
+
+
+                // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+                // Data
+
+                // .............................................................
+                // Data - links
+                var linksInVegaData = [];
+                var encodedChordSequences = data.self.stats.sequences;
+                var support = data.self.stats.support;
+
+                var sequenceId = 0;
+                _.each(encodedChordSequences, function(encodedChordSequence, i) {
+                    var recordsToAdd = [];
+                    var failed = false;
+                    var chordSequenceHasCycles = _.unique(encodedChordSequence).length !== encodedChordSequence.length;
+                    if (!vc.chordSequencesWithCyclesAreIncluded && chordSequenceHasCycles) {
+                        return;
+                    }
+
+                    var parsedEncodedChordSequence = [];
+                    var chordTitles = [];
+                    _.each(encodedChordSequence, function(encodedChord, index) {
+                        if (index > vc.stepCount) {
+                            return;
+                        }
+                        var parsedEncodedChord = renderer.parseEncodedChord(encodedChord);
+                        parsedEncodedChordSequence.push(parsedEncodedChord);
+                        if (index == vc.stepCount) {
+                            chordTitles.push("...");
+                        } else {
+                            chordTitles.push(renderer.titleOfParsedEncodedChord(vc, parsedEncodedChord));
+                        }
+                    });
+
+                    var tooltip = chordTitles.join(" → ") + "<br/>support: " + support[i];
+                    _.each(parsedEncodedChordSequence, function(parsedEncodedChord, indexInSequence) {
+                        if (failed) {
+                            return;
+                        }
+                        if (parsedEncodedChord !== 0 && !((vc.recordingsInMajorModeAreIncluded && parsedEncodedChord[0] == 1)
+                           || (vc.recordingsInMinorModeAreIncluded && parsedEncodedChord[0] == 2))) {
+                            failed = true;
+                            return;
+                        }
+                        var bin = renderer.parsedEncodedChordToBin(vc, parsedEncodedChord);
+                        if (bin === null) {
+                            failed = true;
+                            return;
+                        }
+
+                        recordsToAdd.push({
+                            sequenceId: sequenceId,
+                            encodedChordSequence: encodedChordSequence,
+                            chordSequenceHasCycles: chordSequenceHasCycles,
+                            indexInSequence: indexInSequence,
+                            support: support[i],
+                            chordCoordinate: renderer.chordBinToCoordinate(vc, bin),
+                            color: vc.primaryColor,
+                            tooltip: tooltip
+                        });
+                    });
+                    if (!failed) {
+                        linksInVegaData.push.apply(linksInVegaData, recordsToAdd);
+                    }
+                    ++sequenceId;
+                });
+
+                // Put most frequent on top
+                linksInVegaData.reverse();
+
+                vc.data.push({
+                        "name": "links",
+                        "values": linksInVegaData
+                    });
+
+                // .............................................................
+                // Data - chord names in the secondary axis
+                var chordsInVegaData = [];
+                _.each(vc.sequenceOfUsedRootNotes, function(indexOfRootNote) {
+                   _.each(vc.sequenceOfUsedChordTypes, function(indexOfChordType) {
+                       var bin = renderer.parsedEncodedChordToBin(vc, [0, indexOfRootNote, indexOfChordType]);
+                       chordsInVegaData.push({
+                           rootNoteIndex: indexOfRootNote,
+                           rootNoteTitle: vc.titlesForRootNotes[indexOfRootNote],
+
+                           chordTypeIndex: indexOfChordType,
+                           chordTypeTitle: vc.titlesForChordTypes[indexOfChordType],
+
+                           chordCoordinate: renderer.chordBinToCoordinate(vc, bin)
+                       });
+                   });
+                });
+                vc.data.push({
+                    "name": "chords",
+                    "values": chordsInVegaData,
+                });
+
+                // .............................................................
+                // Data - chord names in the primary axis
+                if (vc.labelsForGroupsAreShown) {
+                    var groupsForVega = [];
+                    if (vc.chordGrouppingIsByType) {
+                        groupsForVega = chordsInVegaData.filter(function(chord) {
+                            return chord.rootNoteIndex == vc.sequenceOfUsedRootNotes[0];
+                        });
+                    } else {
+                        groupsForVega = chordsInVegaData.filter(function(chord) {
+                            return chord.chordTypeIndex == vc.sequenceOfUsedChordTypes[0];
+                        });
+                    }
+                    vc.data.push({
+                        "name": "groups",
+                        "values": groupsForVega,
+                    });
+                }
+
+                // .............................................................
+                // Data - steps
+                var stepsInVegaData = _.range(0, vc.stepCount);
+                vc.data.push({
+                    "name": "steps",
+                    "values": stepsInVegaData,
+                });
+
+
+                // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+                // Scales
+
+                // .............................................................
+                // Scale - steps
+                vc.scales.push({
+                    //"type": "ordinal",
+                    "name": "indexInSequence",
+                    "domainMin": 0,
+                    "domainMax": vc.stepCount - 1,
+                    "point": true,
+                    "round": true,
+                    "range": [0, vc.height]
+                });
+
+                // .............................................................
+                // Scale - line opacity
+                vc.scales.push({
+                    "name": "strokeOpacity",
+                    "type": "linear",
+                    "domain": [0, data.self.coverage["ok_count"] * 2],
+                    "point": true,
+                    "range": [0, 1]
+                });
+
+
+                // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+                // Marks
+
+                // .............................................................
+                // Mark - vertical lines for chords
+                if (true || vc.guidesAreVisible) {
+                    vc.marks.push({
+                        "type": "rect",
+                        "from": {"data": "chords"},
+                        "properties": {
+                            "enter": {
+                                "x": {"field": "chordCoordinate"},
+                                "y": {"value": 0},
+                                "width": {"value": 1},
+                                "height": {"field": {"group": "height"}},
+                                "fill": {"value": vc.colorForAxes}
+                            },
+                        }
+                    });
+                }
+
+                // .............................................................
+                // Mark - vertical lines for N chord
+                if (vc.nIsIncluded && vc.guidesAreVisible) {
+                    vc.marks.push({
+                        "type": "rect",
+                        "properties": {
+                            "enter": {
+                                "x": {"field": {"group": "width"}},
+                                "y": {"value": 0},
+                                "width": {"value": "1"},
+                                "height": {"field": {"group": "height"}},
+                                "fill": {"value": vc.colorForAxes},
+                            },
+                        }
+                    });
+                }
+
+                // .............................................................
+                // Mark - horisontal lines denoting steps
+                if (vc.guidesAreVisible) {
+                    for (var right = 0; right < 2; right++) {
+                        vc.marks.push({
+                            "type": "rect",
+                            "from": {"data": "steps"},
+                            "properties": {
+                                "enter": {
+                                    "x": right ? {"field": {"group": "width"}} : {"value": -vc.sizeOfStepMark},
+                                    "width": {"value": vc.sizeOfStepMark},
+                                    "y": {"scale": "indexInSequence", "field": "data"},
+                                    "height": {"value": 1},
+                                    "fill": {"value": vc.colorForAxes},
+                                }
+                            }
+                        });
+                    }
+                }
+
+                // .............................................................
+                // Mark - links
+                vc.marks.push({
+                    "type": "group",
+                    "from": {
+                        "data": "links",
+                        "transform": [ {
+                            "type": "facet",
+                            "groupby": ["sequenceId"]
+                        } ]
+                    },
+                    "marks": [{
+                        "type": "line",
+                        "properties": {
+                            "enter": {
+                                "x": {"field": "chordCoordinate"},
+                                "y": {"scale": "indexInSequence", "field": "indexInSequence"},
+                                "strokeOpacity": {"scale": "strokeOpacity", "field": "support"},
+                                "stroke": {"field": "color"},
+                                "strokeWidth": {"value": 2},
+                            },
+                            "update": {
+                                "strokeOpacity": {"scale": "strokeOpacity", "field": "support"},
+                                "stroke": {"field": "color"},
+                            },
+                            "hover": {
+                                "strokeOpacity": {"value": 1},
+                                "stroke": {"value": "#000"},
+                            }
+                        },
+                    }]
+                });
+
+                // .............................................................
+                // Mark - group name (primary axis)
+                vc.yOffsetForGroupLabels = -vc.padding.top + 15;
+                vc.yOffsetForChordLabels = -3;
+
+                vc.xOffsetForGroupLabels = 0.5 * vc.chordSize * ((vc.chordGrouppingIsByType ? vc.numberOfRootNotes : vc.numberOfChordTypes) - 1);
+
+                if (vc.labelsForGroupsAreShown) {
+                    vc.marks.push({
+                        "type": "text",
+                        "from": {"data": "groups"},
+                        "properties": {
+                            "enter": {
+                                "x": {"field": "chordCoordinate", "offset": vc.xOffsetForGroupLabels},
+                                "y": {"value": vc.yOffsetForGroupLabels},
+                                "text": {"field": vc.chordGrouppingIsByType ? "chordTypeTitle": "rootNoteTitle"},
+                                "baseline": {"value":"bottom"},
+                                "align": {"value": "center"},
+                                "fill": {"value": vc.colorForAxisLabels},
+                                "font": {"value": vc.fontFace},
+                                "fontSize": {"value": vc.fontSizeForLabelsInAxis},
+                            },
+                        }
+                    });
+                }
+
+                // .............................................................
+                // Mark - column name for N
+                if (vc.nIsIncluded && vc.labelsForGroupsAreShown) {
+                    vc.marks.push({
+                        "type": "text",
+                        "properties": {
+                            "enter": {
+                                "y": {"value": vc.labelsForChordsAreShown ? vc.yOffsetForChordLabels : vc.yOffsetForGroupLabels},
+                                "x": {"field": {"group": "width"}},
+                                "text": {"value":"N"},
+                                "align": {"value":"center"},
+                                "baseline": {"value":"bottom"},
+                                "fill": {"value": vc.colorForAxisLabels},
+                                "font": {"value": vc.fontFace},
+                                "fontSize": {"value": vc.fontSizeForLabelsInAxis},
+                            },
+                        }
+                    });
+                }
+
+                // .............................................................
+                // Mark - chord name (secondary axis)
+                if (vc.labelsForChordsAreShown) {
+                    vc.marks.push({
+                        "type": "text",
+                        "from": {"data": "chords"},
+                        "properties": {
+                            "enter": {
+                                "x": {"field": "data.chordCoordinate", "offset": 0},
+                                "y": {"value": vc.yOffsetForChordLabels},
+                                "text": {"field": vc.chordGrouppingIsByType ? "data.rootNoteTitle": "data.chordTypeTitle"},
+                                "angle": {"value": "-90"},
+                                "baseline": {"value": "middle"},
+                                "fill": {"value": vc.colorForAxisLabels},
+                                "font": {"value": vc.fontFace},
+                                "fontSize": {"value": vc.fontSizeForLabelsInSecondaryAxis},
+                            },
+                        }
+                    });
+                }
+
+                // .............................................................
+                // Mark - fader
+                vc.marks.push({
+                    "type": "image",
+                    "from": {"data": "dummy"},
+                    "properties": {
+                        "enter": {
+                            "x": {"value": 0},
+                            "width": {"field": {"group": "width"}},
+                            "y": {"field": {"group": "height"}},
+                            "height": {"value": vc.padding.bottom + 2},
+                            "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=="},
+                            "fill": {"value": "#F00"},
+                        },
+                    }
+                });
+            }
+        });
+    });
+}, Logger);