Added chart components

Esse commit está contido em:
Alex Castillo
2016-04-26 23:20:46 -04:00
commit 6adfb0ea87
21 arquivos alterados com 557 adições e 8 exclusões
externo
BIN
Ver Arquivo
Arquivo binário não exibido.
+2
Ver Arquivo
@@ -7,3 +7,5 @@ public
coverage
.DS_Store
dist
.DS_Store
+23 -2
Ver Arquivo
@@ -1,11 +1,32 @@
angular.module('OpenEXP', [
'ui.router'
'ui.router',
'chart.js'
])
.config(($stateProvider, $urlRouterProvider) => {
.config(($stateProvider, $urlRouterProvider, ChartJsProvider) => {
$urlRouterProvider.otherwise('/');
ChartJsProvider.setOptions({
animation: false,
responsive: true,
datasetStrokeWidth: 1,
pointDot: false,
pointDotRadius: 1,
pointDotStrokeWidth: 0,
datasetFill: false,
scaleOverride: true,
scaleStartValue: -2,
scaleStepWidth: 1,
scaleSteps: 6,
barShowStroke: false,
barValueSpacing: 1,
barShowStroke: true,
barStrokeWidth: 1,
strokeColor: 'rgba(116,150,161,1)'
});
})
.run(($state) => {
});
+69
Ver Arquivo
@@ -7,3 +7,72 @@
@import '../components/navbar/navbar.scss';
@import './connect/connect.scss';
@import '../app/login/login.scss';
@import url(https://fonts.googleapis.com/css?family=Roboto:400,700,300);
* {
box-sizing: border-box;
}
body {
font-family: 'Roboto', sans-serif;
color: #ffffff;
background-color: #222222;
margin: 0;
}
h1 {
margin: 20px;
font-weight: 300;
}
.row {
display: flex;
}
.block {
display: block;
margin: 20px;
padding: 20px;
box-shadow: 0 0 5px rgba(0,0,0,0.3);
background-color: #333333;
position: relative;
height: auto;
overflow: hidden;
}
.block h2 {
position: absolute;
margin: 0;
top: 10px;
right: 20px;
font-weight: 300;
}
.block-25 {
width: 25%;
}
.block-33 {
width: 33%;
}
.block-50 {
width: 50%;
}
.block-100 {
width: 100%;
}
.time-series-duration {
position: absolute;
width: 110%;
bottom: 30px;
display: flex;
}
.time-series-duration time {
width: 20%;
}
+115 -5
Ver Arquivo
@@ -1,9 +1,119 @@
var dsp = require('dsp.js');
var topogrid = require('topogrid');
angular.module('OpenEXP')
.controller('DashboardCtrl', ['$scope', 'boardFactory', ($scope, boardFactory) => {
.controller('DashboardCtrl', ['$scope', '$timeout', 'boardFactory', ($scope, $timeout, boardFactory) => {
$scope.board = boardFactory.board;
$scope.board = boardFactory.board;
$scope.publish = boardFactory.publish;
$scope.unpublish = boardFactory.unpublish;
$scope.publish = boardFactory.publish;
$scope.unpublish = boardFactory.unpublish;
}]);
var bins = 128; // Approx .5 second
var bufferSize = 128;
var windowRefreshRate = 8;
var windowSize = bins / windowRefreshRate;
var sampleRate = 250;
var sampleNumber = 0;
var signals = [[], [], [], [], [], [], [], []];
var timeSeriesWindow = 5; // in seconds
var timeSeriesRate = 25; // skips every 10 samples
var seriesNumber = 0;
var timeSeries = new Array(8).fill([]); // 8 channels
// the parameters for the grid [x,y,z] where x is the min of the grid, y is the
// max of the grid and z is the number of points
var grid_params = [0, 10, 11];
var pos_x = [3, 7, 2, 8, 0, 10, 3, 7]; // x coordinates of the data
var pos_y = [0, 0, 3, 3, 8, 8, 10, 10]; // y coordinates of the data
// var data = [10,0,0,0,0,0,-10,30,25]; // the data values
timeSeries = timeSeries.map(function (channel) {
return new Array((sampleRate * timeSeriesWindow) / timeSeriesRate).fill(0)
});
boardFactory.board.on('sample', function (sample) {
//console.log('sample', sample);
sampleNumber++;
Object.keys(sample.channelData).forEach(function (channel, i) {
signals[i].push(sample.channelData[channel]);
});
if (sampleNumber === bins) {
var spectrums = [[], [], [], [], [], [], [], []];
signals.forEach(function (signal, index) {
var fft = new dsp.FFT(bufferSize, sampleRate);
fft.forward(signal);
spectrums[index] = parseObjectAsArray(fft.spectrum);
spectrums[index] = voltsToMicrovolts(spectrums[index], true);
});
var scaler = sampleRate / bins;
var labels = new Array(bins / 2).fill()
.map(function (x, i) {
return Math.ceil(i * scaler);
});
var grid = topogrid.create(pos_x, pos_y, sample.channelData, grid_params);
$timeout(function () {
$scope.frequencyData = spectrums;
$scope.frequencyLabels = labels.map(function (label, i) {
return i % 10 === 0 ? label : '';
});
$scope.gridData = [].concat.apply([], grid);
});
signals = signals.map(function (channel) {
return channel.filter(function (signal, index) {
return index > (windowSize - 1);
});
});
sampleNumber = bins - windowSize;
}
seriesNumber++;
// Time Series
if (seriesNumber === timeSeriesRate) {
timeSeries.forEach(function (channel, index) {
channel.push(voltsToMicrovolts(sample.channelData[index]));
channel.shift();
});
$timeout(function () {
$scope.timeData = timeSeries;
$scope.timeLabels = new Array((sampleRate * timeSeriesWindow) / timeSeriesRate).fill(0)
});
seriesNumber = 0;
}
});
function voltsToMicrovolts(volts, log) {
if (!Array.isArray(volts)) volts = [volts];
return volts.map(function (volt) {
return log ? Math.log10(Math.pow(10, 6) * volt) : Math.pow(10, 6) * volt;
});
}
function parseObjectAsArray(obj) {
var array = [];
Object.keys(obj).forEach(function (key) {
array.push(obj[key]);
});
return array;
}
}]);
+10
Ver Arquivo
@@ -2,3 +2,13 @@
<button class="btn btn-lg btn-info" ng-click="publish()"><span class="glyphicon glyphicon-play"></button>
<button class="btn btn-lg btn-warning" ng-click="unpublish()"><span class="glyphicon glyphicon-pause"></button>
</center>
<section class="row">
<bci-frequency ng-if="frequencyData" data="frequencyData" labels="frequencyLabels" class="block block-50"></bci-frequency>
<bci-topo ng-if="gridData" data="gridData" class="block block-50"></bci-topo>
</section>
<section class="row">
<bci-time ng-if="timeData" data="timeData" labels="timeLabels" class="block block-100"></bci-time>
</section>
+1
Ver Arquivo
@@ -7,3 +7,4 @@ angular.module('OpenEXP')
templateUrl: './app/dashboard/dashboard.html'
})
});
+1 -1
Ver Arquivo
@@ -64,7 +64,7 @@ angular.module('OpenEXP')
// each time there is a change of type add, log it
var observer = (changes) => {
changes.forEach(change => {
if(change.type === "add") console.log(change)
//if(change.type === "add") console.log(change)
})
};
+12
Ver Arquivo
@@ -0,0 +1,12 @@
<section>
<h2>Frequency</h2>
<canvas id="frequency"
ng-if="$ctrl.data"
class="chart chart-line"
chart-data="$ctrl.data"
chart-labels="$ctrl.labels"
chart-series="$ctrl.series"
chart-options="$ctrl.options"
chart-colours="$ctrl.colors">
</canvas>
</section>
+46
Ver Arquivo
@@ -0,0 +1,46 @@
angular.module('OpenEXP')
.directive('bciFrequency', function () {
return {
restrict: 'E',
scope: {
data: '=',
labels: '='
},
templateUrl: './components/frequency/frequency.html',
controllerAs: '$ctrl',
bindToController: true,
controller: function () {
var $ctrl = this;
$ctrl.options = {
responsive: true,
animation: true,
animationSteps: 5
};
$ctrl.colors = [
{ strokeColor: 'rgba(112,185,252,1)' },
{ strokeColor: 'rgba(116,150,161,1)' },
{ strokeColor: 'rgba(162,86,178,1)' },
{ strokeColor: 'rgba(144,132,246,1)' },
{ strokeColor: 'rgba(138,219,229,1)' },
{ strokeColor: 'rgba(232,223,133,1)' },
{ strokeColor: 'rgba(148,159,177,1)' },
{ strokeColor: 'rgba(182,224,53,1)' }
];
$ctrl.series = [
'Channel 1',
'Channel 2',
'Channel 3',
'Channel 4',
'Channel 5',
'Channel 6',
'Channel 7',
'Channel 8'
];
}
}
});
+20
Ver Arquivo
@@ -0,0 +1,20 @@
<section class="time-series">
<h2>Time Series</h2>
<canvas ng-repeat="channel in $ctrl.data track by $index"
ng-style="{ position: 'absolute', top: ((60 * $index)) + 'px', padding: '20px 0' }"
height="50"
id="channel1"
ng-if="$ctrl.data"
class="chart chart-line"
chart-data="[channel]"
chart-labels="$ctrl.labels"
chart-options="$ctrl.options"
chart-colours="[$ctrl.colors[$index]]">
</canvas>
<footer class="time-series-duration">
<time ng-repeat="second in [5,4,3,2,1,0] track by $index" datetime="P1M">
<span ng-if="second">-</span>{{ second }}
</time>
</footer>
<div style="height: 600px"></div>
</section>
+50
Ver Arquivo
@@ -0,0 +1,50 @@
angular.module('OpenEXP')
.directive('bciTime', function () {
return {
restrict: 'E',
scope: {
data: '=',
labels: '='
},
templateUrl: './components/time/time.html',
controllerAs: '$ctrl',
bindToController: true,
controller: function () {
var $ctrl = this;
$ctrl.options = {
animation: false,
responsive: true,
showScale: false,
scaleOverride: true,
scaleStartValue: -500,
scaleStepWidth: 1,
scaleSteps: 1500
};
$ctrl.colors = [
{ strokeColor: 'rgba(112,185,252,1)' },
{ strokeColor: 'rgba(116,150,161,1)' },
{ strokeColor: 'rgba(162,86,178,1)' },
{ strokeColor: 'rgba(144,132,246,1)' },
{ strokeColor: 'rgba(138,219,229,1)' },
{ strokeColor: 'rgba(232,223,133,1)' },
{ strokeColor: 'rgba(148,159,177,1)' },
{ strokeColor: 'rgba(182,224,53,1)' }
];
$ctrl.series = [
'Channel 1',
'Channel 2',
'Channel 3',
'Channel 4',
'Channel 5',
'Channel 6',
'Channel 7',
'Channel 8'
];
}
}
});
+82
Ver Arquivo
@@ -0,0 +1,82 @@
* {
box-sizing: border-box;
}
.topoplot-wrapper {
width: 300px;
height: 300px;
border: 3px solid black;
border-radius: 50%;
position: relative;
}
[class*='topoplot-c'] {
z-index: 2;
background-color: black;
border: 1px solid black;
width: 5%;
height: 5%;
border-radius: 50%;
position: absolute;
}
.top {
top: 2%
}
.middle {
top: calc(50% - 50px);
}
.third {
top: 70%;
}
.bottom {
bottom: 2%;
}
.top.left,
.bottom.left {
left: 32%;
}
.top.right,
.bottom.right {
right: 32%;
}
.middle.left {
left: 25%;
}
.middle.right {
right: 25%;
}
.third.left {
left: 8%;
}
.third.right {
right: 8%;
}
/* Grid */
.topoplot-grid {
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
-webkit-clip-path: circle(50%);
border-radius: 50%;
-webkit-filter: blur(10px);
}
[class*='topoplot-u'] {
float: left;
/*border: 1px solid lightgray;*/
width: 9.09%;
height: 9.09%;
/*background: lightblue;*/
}
+19
Ver Arquivo
@@ -0,0 +1,19 @@
<section>
<h2>Topo</h2>
<section class="topoplot-wrapper">
<article class="topoplot-c1 top left"></article>
<article class="topoplot-c2 top right"></article>
<article class="topoplot-c3 middle left"></article>
<article class="topoplot-c4 middle right"></article>
<article class="topoplot-c5 third left"></article>
<article class="topoplot-c6 third right"></article>
<article class="topoplot-c7 bottom left"></article>
<article class="topoplot-c8 bottom right"></article>
<aside class="topoplot-grid">
<div ng-repeat="pixel in $ctrl.data track by $index" ng-style="$ctrl.getColor($index,pixel,$ctrl.data)" ng-class="$ctrl.getClass($index)"></div>
</aside>
</section>
</section>
+28
Ver Arquivo
@@ -0,0 +1,28 @@
angular.module('OpenEXP')
.directive('bciTopo', function () {
return {
restrict: 'E',
scope: {
data: '='
},
templateUrl: './components/topo/topo.html',
controllerAs: '$ctrl',
bindToController: true,
controller: function () {
var $ctrl = this;
$ctrl.getClass = function(index){
return 'topoplot-u' + index;
};
$ctrl.getColor = function(index, pixel, grid) {
var min = Math.min.apply(Math, grid);
var max = Math.max.apply(Math, grid);
var f = chroma.scale('Spectral').domain([min, max]);
return {'background-color': f(pixel)}
};
}
}
});
+6
Ver Arquivo
@@ -3,6 +3,9 @@
<meta charset="UTF-8">
<title>OpenEXP</title>
<link rel="stylesheet" href="http://cdn.jsdelivr.net/angular.chartjs/latest/angular-chart.css">
<link rel="stylesheet" href="components/topo/topo.css">
</head>
<body ng-app="OpenEXP">
<header id="nav">
@@ -14,6 +17,9 @@
<!--COMMENT OUT NEXT THREE LINES FOR PRODUCTION-->
<script src="http://localhost:8080/webpack-dev-server.js"></script>
<script src="http://localhost:8080/build/vendor.js"></script>
<script src="lib/chroma.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.1.1/Chart.js"></script>
<script src="http://cdn.jsdelivr.net/angular.chartjs/latest/angular-chart.min.js"></script>
<script src="http://localhost:8080/build/app.js"></script>
<!--UNCOMMENT NEXT TWO LINES FOR PRODUCTION-->
+3
Ver Arquivo
@@ -5,6 +5,9 @@ module.exports = function() {
require('./app/app');
// FACTORY METHODS
require('./components/frequency/frequency.js');
require('./components/time/time.js');
require('./components/topo/topo.js');
require('./components/board/boardFactory.js');
// PAGES
+30
Ver Arquivo
@@ -0,0 +1,30 @@
var EEGSpectrumUtils = {
/**
* filterBand: Give spectrums and labels, it filters the spectrums based on the labels within the range
* @param spectrums
* @param labels
* @param range
* @returns {{spectrums: Array, labels: *}}
*/
filterBand: function (spectrums, labels, range) {
if (!spectrums ) return console.log('Please provide spectrums');
spectrums = spectrums.map(function (channel) {
return channel.filter(function (spectrum, index) {
return labels[index] >= range[0] && labels[index] <= range[1];
});
});
spectrums = [spectrums.map(function (channel) {
if (channel.length) {
return channel.reduce(function (a, b) {
return a + b;
}) / channel.length;
} else return channel;
})];
return {
spectrums: spectrums,
labels: labels
}
}
};
+33
Ver Arquivo
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
+3
Ver Arquivo
@@ -6,6 +6,9 @@ module.exports = function() {
// JS
require('script!angular/angular.js');
// VERSIONS ARE NO GOOD
//require('script!chart.js/Chart.js');
//require('script!angular-chartjs/dist/angular-chartjs.js');
require('script!angular-ui-router/release/angular-ui-router.js');
require('script!bootstrap/dist/js/bootstrap.min.js');
require('script!jspsych/jspsych.js');
+4
Ver Arquivo
@@ -71,15 +71,19 @@
},
"dependencies": {
"angular": "1.5.0-rc.0",
"angular-chartjs": "0.0.5",
"angular-ui-bootstrap": "^0.14.3",
"angular-ui-router": "^0.2.15",
"babel": "^6.3.13",
"bootstrap": "^3.3.6",
"chart.js": "^1.1.1",
"dsp.js": "github:neurojs/dsp.js",
"gulp": "^3.9.0",
"jquery": "^2.1.4",
"jspsych": "git://github.com/teonlamont/jsPsych.git#590313c2fc528c147a5d6040f001c7407ee3623b",
"ngmin": "^0.5.0",
"openbci-sdk": "^0.2.0",
"topogrid": "^1.0.6",
"url-loader": "^0.5.7"
}
}