tsviewer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/babel.config.js +5 -0
- package/dist/demo.html +1 -0
- package/dist/tsviewer.common.js +33247 -0
- package/dist/tsviewer.common.js.map +1 -0
- package/dist/tsviewer.umd.js +33259 -0
- package/dist/tsviewer.umd.js.map +1 -0
- package/dist/tsviewer.umd.min.js +4 -0
- package/dist/tsviewer.umd.min.js.map +1 -0
- package/jsconfig.json +19 -0
- package/package.json +61 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +17 -0
- package/src/App.vue +41 -0
- package/src/assets/icons/blackfynn-amplitude-zoom.js +11 -0
- package/src/assets/icons/blackfynn-timescale.js +11 -0
- package/src/assets/icons/icon-controller-pause.js +11 -0
- package/src/assets/icons/icon-controller-play.js +11 -0
- package/src/assets/icons/icon-next-page.js +11 -0
- package/src/assets/icons/icon-previous-page.js +11 -0
- package/src/assets/icons/icon-stopwatch.js +11 -0
- package/src/assets/icons/index.js +7 -0
- package/src/components/TSPlotCanvas.vue +1868 -0
- package/src/components/TSScrubber.vue +420 -0
- package/src/components/TSViewer.vue +595 -0
- package/src/components/TSViewerCanvas.vue +793 -0
- package/src/components/TSViewerToolbar.vue +356 -0
- package/src/components/index.js +13 -0
- package/src/main.js +23 -0
- package/src/mixins/request/index.js +100 -0
- package/src/mixins/ts-annotation/index.js +202 -0
- package/src/mixins/viewer-active-tool/index.js +36 -0
- package/src/store/index.js +184 -0
- package/src/utils/constants.js +15 -0
- package/vue.config.js +12 -0
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
ref="ts_viewer"
|
|
4
|
+
class="timeseries-viewer"
|
|
5
|
+
>
|
|
6
|
+
<timeseries-scrubber
|
|
7
|
+
ref="scrubber"
|
|
8
|
+
:ts_start="ts_start"
|
|
9
|
+
:ts_end="ts_end"
|
|
10
|
+
:c-width="cWidth"
|
|
11
|
+
:label-width="labelWidth"
|
|
12
|
+
:cursor-loc="cursorLoc"
|
|
13
|
+
:start="start"
|
|
14
|
+
:duration="duration"
|
|
15
|
+
:constants="constants"
|
|
16
|
+
@setStart="updateStart"
|
|
17
|
+
/>
|
|
18
|
+
|
|
19
|
+
<div id="channelCanvas">
|
|
20
|
+
<!-- Channel labels -->
|
|
21
|
+
<div
|
|
22
|
+
id="channelLabels"
|
|
23
|
+
ref="channelLabels"
|
|
24
|
+
:style="channelStyle"
|
|
25
|
+
>
|
|
26
|
+
<div
|
|
27
|
+
v-for="item in viewerChannels"
|
|
28
|
+
v-show="item.visible"
|
|
29
|
+
:key="item.label"
|
|
30
|
+
>
|
|
31
|
+
<div
|
|
32
|
+
class="chLabelWrap"
|
|
33
|
+
:data-id="item.id"
|
|
34
|
+
@tap="onLabelTap"
|
|
35
|
+
>
|
|
36
|
+
<div class="labelDiv" :style="_cpStyleLabels">
|
|
37
|
+
{{ item.label }}
|
|
38
|
+
</div>
|
|
39
|
+
<div
|
|
40
|
+
class="chLabelIndWrap"
|
|
41
|
+
v-if="!hideLabelInfo"
|
|
42
|
+
:selected="item.selected"
|
|
43
|
+
>
|
|
44
|
+
<div
|
|
45
|
+
class="chLabelInd"
|
|
46
|
+
|
|
47
|
+
>
|
|
48
|
+
{{ _computeLabelInfo(item, globalZoomMult, item.rowScale) }}
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<!-- Timeseries viewport -->
|
|
56
|
+
<timeseries-viewer-canvas
|
|
57
|
+
ref="viewerCanvas"
|
|
58
|
+
:window_height="window_height"
|
|
59
|
+
:window_width="window_width"
|
|
60
|
+
:duration="duration"
|
|
61
|
+
:start="start"
|
|
62
|
+
:c-width="cWidth"
|
|
63
|
+
:c-height="cHeight"
|
|
64
|
+
:constants="constants"
|
|
65
|
+
:ts-start="ts_start"
|
|
66
|
+
:ts-end="ts_end"
|
|
67
|
+
:cursor-loc="cursorLoc"
|
|
68
|
+
:global-zoom-mult="globalZoomMult"
|
|
69
|
+
@setStart="updateStart"
|
|
70
|
+
@setCursor="setCursor"
|
|
71
|
+
@setGlobalZoom="setGlobalZoom"
|
|
72
|
+
@setDuration="setDuration"
|
|
73
|
+
@channelsInitialized="onChannelsInitialized"
|
|
74
|
+
@annLayersInitialized="onAnnLayersInitialized"
|
|
75
|
+
@closeAnnotationLayerWindow="onCloseAnnotationLayerWindow"
|
|
76
|
+
@addAnnotation="onOpenAnnotationWindow"
|
|
77
|
+
@updateAnnotation="onUpdateAnnotation"
|
|
78
|
+
/>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<timeseries-viewer-toolbar
|
|
82
|
+
:constants="constants"
|
|
83
|
+
:duration="duration"
|
|
84
|
+
:start="start"
|
|
85
|
+
@pageBack="onPageBack"
|
|
86
|
+
@pageForward="onPageForward"
|
|
87
|
+
@incrementZoom="onIncrementZoom"
|
|
88
|
+
@decrementZoom="onDecrementZoom"
|
|
89
|
+
@updateDuration="onUpdateDuration"
|
|
90
|
+
@nextAnnotation="onNextAnnotation"
|
|
91
|
+
@previousAnnotation="onPreviousAnnotation"
|
|
92
|
+
@setStart="updateStart"
|
|
93
|
+
/>
|
|
94
|
+
|
|
95
|
+
<!-- <timeseries-filter-modal-->
|
|
96
|
+
<!-- ref="filterWindow"-->
|
|
97
|
+
<!-- :filter-window-open="filterWindowOpen"-->
|
|
98
|
+
<!-- @closeWindow="onCloseFilterWindow"-->
|
|
99
|
+
<!-- />-->
|
|
100
|
+
|
|
101
|
+
<!-- <timeseries-annotation-modal-->
|
|
102
|
+
<!-- ref="annotationModal"-->
|
|
103
|
+
<!-- :visible.sync="annotationWindowOpen"-->
|
|
104
|
+
<!-- @closeWindow="onCloseAnnotationWindow"-->
|
|
105
|
+
<!-- @createUpdateAnnotation="onCreateUpdateAnnotation"-->
|
|
106
|
+
<!-- />-->
|
|
107
|
+
|
|
108
|
+
<!-- <timeseries-annotation-layer-modal-->
|
|
109
|
+
<!-- ref="layerModal"-->
|
|
110
|
+
<!-- :annotation-layer-window-open="annotationLayerWindowOpen"-->
|
|
111
|
+
<!-- @closeWindow="onCloseAnnotationLayerWindow"-->
|
|
112
|
+
<!-- @createLayer="onCreateAnnotationLayer"-->
|
|
113
|
+
<!-- />-->
|
|
114
|
+
|
|
115
|
+
</div>
|
|
116
|
+
</template>
|
|
117
|
+
|
|
118
|
+
<script>
|
|
119
|
+
import {
|
|
120
|
+
head,
|
|
121
|
+
propOr,
|
|
122
|
+
isEmpty
|
|
123
|
+
} from 'ramda'
|
|
124
|
+
import ViewerActiveTool from '@/mixins/viewer-active-tool'
|
|
125
|
+
import Request from '@/mixins/request'
|
|
126
|
+
import TsAnnotation from '@/mixins/ts-annotation'
|
|
127
|
+
import TimeseriesScrubber from '@/components/TSScrubber.vue'
|
|
128
|
+
import TimeseriesViewerCanvas from '@/components/TSViewerCanvas.vue'
|
|
129
|
+
import TimeseriesViewerToolbar from '@/components/TSViewerToolbar.vue'
|
|
130
|
+
export default {
|
|
131
|
+
name: 'TimeseriesViewer',
|
|
132
|
+
components:{
|
|
133
|
+
TimeseriesScrubber,
|
|
134
|
+
TimeseriesViewerCanvas,
|
|
135
|
+
TimeseriesViewerToolbar
|
|
136
|
+
//'timeseries-filter-modal': () => import('@/components/viewers/TSViewer/TSFilterModal.vue'),
|
|
137
|
+
//'timeseries-annotation-layer-modal': () => import('@/components/viewers/TSViewer/TSViewerLayerWindow.vue'),
|
|
138
|
+
//'timeseries-annotation-modal': () => import('@/components/viewers/TSViewer/TSAnnotationModal.vue')
|
|
139
|
+
},
|
|
140
|
+
mixins: [
|
|
141
|
+
Request,
|
|
142
|
+
ViewerActiveTool,
|
|
143
|
+
TsAnnotation,
|
|
144
|
+
],
|
|
145
|
+
watch: {
|
|
146
|
+
viewerSidePanelOpen: {
|
|
147
|
+
handler: function() {
|
|
148
|
+
// console.log('resized triggered')
|
|
149
|
+
this.onResize()
|
|
150
|
+
},
|
|
151
|
+
immediate: true
|
|
152
|
+
},
|
|
153
|
+
userToken: {
|
|
154
|
+
handler: async function(token) {
|
|
155
|
+
await this.$store.dispatch('updateUserToken', token)
|
|
156
|
+
},
|
|
157
|
+
immediate: true
|
|
158
|
+
},
|
|
159
|
+
packageId: {
|
|
160
|
+
handler: async function(id) {
|
|
161
|
+
await this.$store.dispatch('openViewer', {
|
|
162
|
+
packageId: id,
|
|
163
|
+
packageType: this.packageType
|
|
164
|
+
})
|
|
165
|
+
},
|
|
166
|
+
immediate: true
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
props: {
|
|
170
|
+
userToken: {
|
|
171
|
+
type: String,
|
|
172
|
+
default: () => ''
|
|
173
|
+
},
|
|
174
|
+
packageId: {
|
|
175
|
+
type: String,
|
|
176
|
+
default: () => ''
|
|
177
|
+
},
|
|
178
|
+
packageType: {
|
|
179
|
+
type: String,
|
|
180
|
+
default: () => ''
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
computed: {
|
|
184
|
+
viewerChannels: function() {
|
|
185
|
+
return this.$store.getters.viewerChannels
|
|
186
|
+
},
|
|
187
|
+
viewerAnnotations: function() {
|
|
188
|
+
return this.$store.getters.viewerAnnotations
|
|
189
|
+
},
|
|
190
|
+
viewerSidePanelOpen: function() {
|
|
191
|
+
return this.$store.getters.viewerSidePanelOpen
|
|
192
|
+
},
|
|
193
|
+
viewerMontageScheme: function() {
|
|
194
|
+
return this.$store.getters.viewerMontageScheme
|
|
195
|
+
},
|
|
196
|
+
channelStyle: function() {
|
|
197
|
+
return {
|
|
198
|
+
height: this.window_height - 110 + 'px'
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
hideLabelInfo: function() {
|
|
202
|
+
let hide = false;
|
|
203
|
+
const nrChannels = this.nrVisChannels
|
|
204
|
+
if (this.cHeight/nrChannels < 30) {
|
|
205
|
+
hide = true;
|
|
206
|
+
}
|
|
207
|
+
return hide;
|
|
208
|
+
},
|
|
209
|
+
nrVisChannels: function() {
|
|
210
|
+
return this.viewerChannels.reduce((accumulator, currentValue) => {
|
|
211
|
+
if(currentValue.visible) {
|
|
212
|
+
return accumulator + 1
|
|
213
|
+
}
|
|
214
|
+
return accumulator
|
|
215
|
+
}, 0)
|
|
216
|
+
},
|
|
217
|
+
_cpStyleLabels: function() {
|
|
218
|
+
const h = Math.max(1, Math.min(12, (this.window_height - 110)/this.nrVisChannels-2));
|
|
219
|
+
return 'font-size:' + h + 'px; height:' + h + 'px';
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
data: function () {
|
|
223
|
+
return {
|
|
224
|
+
constants: {
|
|
225
|
+
TIMEUNIT: 'microSeconds', // Basis for time
|
|
226
|
+
XOFFSET: 0, // X-offset of graph in canvas
|
|
227
|
+
XGRIDSPACING: 1000000, // Time in microseconds between vertical lines
|
|
228
|
+
NRPXPERLABEL: 150, // Number of pixels per label on x-axis
|
|
229
|
+
USEREALTIME: true, // If true than interpret timepoints as UTC microseconds.
|
|
230
|
+
DEFAULTDPI: 96, // Default pixels per inch
|
|
231
|
+
ANNOTATIONLABELHEIGHT: 20, // Height of annotation label
|
|
232
|
+
ROUNDDATAPIXELS: false, // If true, canvas point will be rounded to integer pixels for faster render (faster)
|
|
233
|
+
MINMAXPOLYGON: true, // If true, then polygon is rendered thru minMax values, otherwise vertical lines (faster)
|
|
234
|
+
PAGESIZEDIVIDER: 0.5, // Number of pages that span the current canvas.
|
|
235
|
+
PREFETCHPAGES: 2, // Number of pages to read ahead of view.
|
|
236
|
+
LIMITANNFETCH: 500, // Maximum number of annotations that are fetched per request
|
|
237
|
+
USEMEDIAN: false, // Use Median instead of mean for centering channels
|
|
238
|
+
CURSOROFFSET: 5, // Offset of cursor canvas
|
|
239
|
+
SEGMENTSPAN: 1209600000000, // One week of gap-data is returned per request.
|
|
240
|
+
MAXRECURSION: 20, // Maximum recursion depth of gap-data requests (max 2 years)
|
|
241
|
+
MAXDURATION: 600000000, // Maximum duration window (5min)
|
|
242
|
+
INITDURATION: 15000000 // Initial duration window (15sec)
|
|
243
|
+
},
|
|
244
|
+
summary: {},
|
|
245
|
+
ts_start: null,
|
|
246
|
+
ts_end: null,
|
|
247
|
+
window_height: 0,
|
|
248
|
+
window_width:0,
|
|
249
|
+
start:0, // Start Timestamp of viewer in microseconds
|
|
250
|
+
duration: 0, // Length of data in viewer in microseconds (ignore gaps)
|
|
251
|
+
cWidth: 0,
|
|
252
|
+
cHeight: 0,
|
|
253
|
+
labelWidth:0,
|
|
254
|
+
globalZoomMult: 1,
|
|
255
|
+
cursorLoc: 1/10,
|
|
256
|
+
filterWindowOpen: false,
|
|
257
|
+
annotationWindowOpen: false,
|
|
258
|
+
annotationLayerWindowOpen: false,
|
|
259
|
+
annotationDelete: null,
|
|
260
|
+
isTsAnnotationDeleteDialogVisible: false,
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
mounted: function () {
|
|
264
|
+
this.window_height = window.innerHeight - 144 - 250;
|
|
265
|
+
this.window_width = this.$refs.ts_viewer.offsetWidth
|
|
266
|
+
window.addEventListener('resize', this.onResize)
|
|
267
|
+
const labelDiv = this.$refs.channelLabels
|
|
268
|
+
this.labelWidth = labelDiv.clientWidth
|
|
269
|
+
this.cWidth = (this.window_width - labelDiv.clientWidth - 5 - 10)
|
|
270
|
+
this.cHeight = (this.window_height - 88)
|
|
271
|
+
this.duration = this.constants['INITDURATION']
|
|
272
|
+
},
|
|
273
|
+
beforeDestroy() {
|
|
274
|
+
window.removeEventListener('resize', this.onResize)
|
|
275
|
+
},
|
|
276
|
+
methods: {
|
|
277
|
+
openEditAnnotationDialog: function(annotation) {
|
|
278
|
+
this.$store.dispatch('setActiveAnnotation', annotation).then(() =>{
|
|
279
|
+
this.$refs.viewerCanvas.renderAnnotationCanvas()
|
|
280
|
+
this.annotationWindowOpen = true
|
|
281
|
+
})
|
|
282
|
+
},
|
|
283
|
+
onUpdateAnnotation: function(annotation) {
|
|
284
|
+
this.openEditAnnotationDialog(annotation)
|
|
285
|
+
},
|
|
286
|
+
onCreateUpdateAnnotation: function(annotation) {
|
|
287
|
+
this.annotationWindowOpen = false
|
|
288
|
+
if (annotation.id) {
|
|
289
|
+
this.updateAnnotation()
|
|
290
|
+
} else {
|
|
291
|
+
this.addAnnotation()
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
onAnnotationUpdated: function() {
|
|
295
|
+
this.$refs.viewerCanvas.renderAnnotationCanvas()
|
|
296
|
+
},
|
|
297
|
+
onOpenAnnotationWindow: function() {
|
|
298
|
+
this.annotationWindowOpen = true
|
|
299
|
+
},
|
|
300
|
+
confirmDeleteAnnotation: function(annotation) {
|
|
301
|
+
this.annotationDelete = annotation
|
|
302
|
+
this.isTsAnnotationDeleteDialogVisible = true
|
|
303
|
+
},
|
|
304
|
+
deleteAnnotation: function(annotation) {
|
|
305
|
+
this.isTsAnnotationDeleteDialogVisible = false
|
|
306
|
+
this.removeAnnotation(annotation)
|
|
307
|
+
},
|
|
308
|
+
onAnnotationDeleted: function() {
|
|
309
|
+
this.$refs.viewerCanvas.renderAnnotationCanvas()
|
|
310
|
+
},
|
|
311
|
+
onAddAnnotation: function (start, duration, onAll, label, description, layer) {
|
|
312
|
+
this.addAnnotation(start, duration, onAll, label, description, layer)
|
|
313
|
+
},
|
|
314
|
+
onAnnotationCreated: function() {
|
|
315
|
+
this.$refs.viewerCanvas.renderAnnotationCanvas()
|
|
316
|
+
},
|
|
317
|
+
onCreateAnnotationLayer: function (newLayer) {
|
|
318
|
+
this.$refs.viewerCanvas.createAnnotationLayer(newLayer)
|
|
319
|
+
},
|
|
320
|
+
onCloseAnnotationLayerWindow: function() {
|
|
321
|
+
this.annotationLayerWindowOpen = false
|
|
322
|
+
},
|
|
323
|
+
onCloseAnnotationWindow: function() {
|
|
324
|
+
this.$refs.viewerCanvas.resetFocusedAnnotation()
|
|
325
|
+
this.$refs.viewerCanvas.renderAnnotationCanvas()
|
|
326
|
+
this.annotationWindowOpen = false
|
|
327
|
+
},
|
|
328
|
+
onCloseFilterWindow: function() {
|
|
329
|
+
this.filterWindowOpen = false
|
|
330
|
+
},
|
|
331
|
+
onLabelTap: function(e) {
|
|
332
|
+
e.stopPropagation();
|
|
333
|
+
e.preventDefault();
|
|
334
|
+
const append = e.detail.sourceEvent.metaKey;
|
|
335
|
+
this.selectChannel({channelId: e.currentTarget.dataset.id, append:append});
|
|
336
|
+
this.$refs.viewerCanvas.renderAll()
|
|
337
|
+
},
|
|
338
|
+
onNextAnnotation: function () {
|
|
339
|
+
this.start = this.$refs.viewerCanvas.getNextAnnotation()
|
|
340
|
+
},
|
|
341
|
+
onPreviousAnnotation: function () {
|
|
342
|
+
this.start = this.$refs.viewerCanvas.getPreviousAnnotation()
|
|
343
|
+
},
|
|
344
|
+
onUpdateDuration: function(value) {
|
|
345
|
+
this.setDuration(value * 1e6)
|
|
346
|
+
},
|
|
347
|
+
onIncrementZoom: function () {
|
|
348
|
+
this.globalZoomMult = this.globalZoomMult * 1.25
|
|
349
|
+
},
|
|
350
|
+
onDecrementZoom: function () {
|
|
351
|
+
this.globalZoomMult = this.globalZoomMult * 0.8
|
|
352
|
+
},
|
|
353
|
+
onAnnLayersInitialized: function () {
|
|
354
|
+
this.$refs.scrubber.getAnnotations()
|
|
355
|
+
},
|
|
356
|
+
onChannelsInitialized: function (channels) {
|
|
357
|
+
this.initViewerStart((channels))
|
|
358
|
+
// TODO: Bring back
|
|
359
|
+
// this.$refs.scrubber.initSegmentSpans()
|
|
360
|
+
// Resize the canvas as label length likely changed
|
|
361
|
+
this.$nextTick(() => {
|
|
362
|
+
this.onResize()
|
|
363
|
+
})
|
|
364
|
+
},
|
|
365
|
+
onPageBack: function() {
|
|
366
|
+
//TODO: Update logic to track gap over all channels
|
|
367
|
+
let setStart = this.start - (3*this.duration)/4
|
|
368
|
+
|
|
369
|
+
//TODO: Bring back when we have segment sections information
|
|
370
|
+
// let channelOneSegments = this.viewerChannels[0].dataSegments
|
|
371
|
+
//
|
|
372
|
+
// let i = 0;
|
|
373
|
+
// for(let segment in channelOneSegments) {
|
|
374
|
+
// if (channelOneSegments[segment] > setStart) {
|
|
375
|
+
// break
|
|
376
|
+
// }
|
|
377
|
+
// i++
|
|
378
|
+
// }
|
|
379
|
+
//
|
|
380
|
+
// // If new page completely in gap --> set start to next timestamp with data
|
|
381
|
+
// if(i % 2 == 0) {
|
|
382
|
+
// setStart = channelOneSegments[i-1] - 0.5*this.duration
|
|
383
|
+
// }
|
|
384
|
+
this.start = setStart
|
|
385
|
+
},
|
|
386
|
+
onPageForward: function() {
|
|
387
|
+
//TODO: Update logic to track gap over all channels
|
|
388
|
+
let setStart = this.start + (3*this.duration)/4
|
|
389
|
+
|
|
390
|
+
//TODO: Bring back when we have segment sections information
|
|
391
|
+
// let channelOneSegments = this.viewerChannels[0].dataSegments
|
|
392
|
+
//
|
|
393
|
+
// let i = 0;
|
|
394
|
+
// for(let segment in channelOneSegments) {
|
|
395
|
+
// if (channelOneSegments[segment] > setStart) {
|
|
396
|
+
// break
|
|
397
|
+
// }
|
|
398
|
+
// i++
|
|
399
|
+
// }
|
|
400
|
+
//
|
|
401
|
+
// // If new page completely in gap --> set start to next timestamp with data
|
|
402
|
+
// if(i % 2 == 0) {
|
|
403
|
+
// setStart = channelOneSegments[i] - 0.5*this.duration
|
|
404
|
+
// }
|
|
405
|
+
|
|
406
|
+
this.start = setStart
|
|
407
|
+
},
|
|
408
|
+
selectAnnotation: function(payload) {
|
|
409
|
+
let rsPeriod = this.$refs.viewerCanvas.rsPeriod
|
|
410
|
+
this.updateStart(payload.annotation.start - ((this.cursorLoc*this.cWidth - this.constants['CURSOROFFSET']) * rsPeriod))
|
|
411
|
+
},
|
|
412
|
+
selectChannel: function(payload) {
|
|
413
|
+
const _channels = this.viewerChannels.map(channel => {
|
|
414
|
+
const selected = channel.selected
|
|
415
|
+
if (payload['append'] === false) {
|
|
416
|
+
channel.selected = false
|
|
417
|
+
}
|
|
418
|
+
if (payload['channelId'] === channel.id) {
|
|
419
|
+
channel.selected = !selected
|
|
420
|
+
}
|
|
421
|
+
return channel
|
|
422
|
+
})
|
|
423
|
+
this.$store.dispatch('setChannels', _channels)
|
|
424
|
+
},
|
|
425
|
+
selectChannels: function(ids, append) {
|
|
426
|
+
const channels = this.viewerChannels.map(channel => {
|
|
427
|
+
if (append === false) {
|
|
428
|
+
channel.selected = false
|
|
429
|
+
}
|
|
430
|
+
if( channel.id in ids) {
|
|
431
|
+
channel.selected = true
|
|
432
|
+
}
|
|
433
|
+
return channel
|
|
434
|
+
})
|
|
435
|
+
this.$store.dispatch('setChannels', channels)
|
|
436
|
+
},
|
|
437
|
+
updateStart: function(value) {
|
|
438
|
+
// console.log('setting start to: ' + value)
|
|
439
|
+
this.start = value
|
|
440
|
+
},
|
|
441
|
+
setCursor: function(value) {
|
|
442
|
+
// set the cursor location as a fraction of the width of the canvas
|
|
443
|
+
this.cursorLoc = value
|
|
444
|
+
},
|
|
445
|
+
setGlobalZoom: function(value) {
|
|
446
|
+
// console.log('setGlobalZoom')
|
|
447
|
+
this.globalZoomMult = value
|
|
448
|
+
},
|
|
449
|
+
setDuration: function(value) {
|
|
450
|
+
if (value > this.constants['MAXDURATION']) {
|
|
451
|
+
this.duration = this.constants['MAXDURATION']
|
|
452
|
+
} else {
|
|
453
|
+
this.duration = value
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
getChannelId: function(channel) {
|
|
457
|
+
const isViewingMontage = this.viewerMontageScheme !== 'NOT_MONTAGED'
|
|
458
|
+
let id = propOr('', 'id', channel)
|
|
459
|
+
let list = []
|
|
460
|
+
if (isViewingMontage) {
|
|
461
|
+
list = id.split('_')
|
|
462
|
+
id = list.length ? head(list) : id // remove channel name from id
|
|
463
|
+
}
|
|
464
|
+
return id
|
|
465
|
+
},
|
|
466
|
+
onResize() {
|
|
467
|
+
if (this.$refs.ts_viewer === undefined) {
|
|
468
|
+
return
|
|
469
|
+
}
|
|
470
|
+
this.window_height = window.innerHeight - 144 - 250;
|
|
471
|
+
this.window_width = this.$refs.ts_viewer.offsetWidth
|
|
472
|
+
const labelDiv = this.$refs.channelLabels;
|
|
473
|
+
this.labelWidth = labelDiv.clientWidth
|
|
474
|
+
this.cWidth = (this.window_width - labelDiv.clientWidth - 16);
|
|
475
|
+
this.cHeight = (this.window_height - 88);
|
|
476
|
+
},
|
|
477
|
+
_computeLabelInfo: function(item, globalZoomMult, rowscale) {
|
|
478
|
+
const n = ( ( (this.constants['DEFAULTDPI'] * window.devicePixelRatio)/(globalZoomMult * rowscale) )/25.4).toFixed(1);
|
|
479
|
+
return n+ ' ' + item.unit + '/mm'
|
|
480
|
+
},
|
|
481
|
+
initViewerStart: function(channels) {
|
|
482
|
+
// const channels = this.activeViewer.channels
|
|
483
|
+
if (channels.length > 0) {
|
|
484
|
+
// Find Global start and end
|
|
485
|
+
this.ts_start = channels[0].content.start
|
|
486
|
+
this.ts_end = channels[0].content.end
|
|
487
|
+
for (let ic = 1; ic<channels.length; ic++) {
|
|
488
|
+
if (channels[ic].content.start < this.ts_start) {
|
|
489
|
+
this.ts_start = channels[ic].content.start
|
|
490
|
+
}
|
|
491
|
+
if (channels[ic].content.end > this.ts_end) {
|
|
492
|
+
this.ts_end = channels[ic].content.end
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
this.start = this.ts_start
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
openLayerWindow: function(payload) {
|
|
499
|
+
const layerModal = this.$refs.layerModal
|
|
500
|
+
layerModal.isCreating = payload.isCreating
|
|
501
|
+
if (!payload.isCreating) {
|
|
502
|
+
layerModal.layer = payload.layer
|
|
503
|
+
} else {
|
|
504
|
+
layerModal.layer = {}
|
|
505
|
+
// layerModal.setColorByIndex(this.viewerAnnotations.length % layerModal.colorOptions.length)
|
|
506
|
+
}
|
|
507
|
+
this.annotationLayerWindowOpen = true
|
|
508
|
+
},
|
|
509
|
+
openFilterWindow: function(payload) {
|
|
510
|
+
const channels = propOr([], 'channels', payload);
|
|
511
|
+
const filter = propOr('', 'filter', payload);
|
|
512
|
+
const filterWindow = this.$refs.filterWindow;
|
|
513
|
+
filterWindow.onChannels = channels;
|
|
514
|
+
if (!isEmpty(filter)) {
|
|
515
|
+
filterWindow.input0 = filter.input0;
|
|
516
|
+
filterWindow.input1 = filter.input1;
|
|
517
|
+
for (let i=0; i<filterWindow._filters.length; i++) {
|
|
518
|
+
if (filterWindow._filters[i].value === filter.type) {
|
|
519
|
+
filterWindow.selectedFilter = filter.type;
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
for (let i=0; i<filterWindow._notchValues.length; i++) {
|
|
524
|
+
if (filterWindow._notchValues[i].value === filter.notchFreq) {
|
|
525
|
+
filterWindow.selectedNotch = filter.notchFreq;
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
} else {
|
|
530
|
+
filterWindow.input0 = NaN;
|
|
531
|
+
filterWindow.input1 = NaN;
|
|
532
|
+
filterWindow.selectedFilter = null;
|
|
533
|
+
filterWindow.selectedNotch = null;
|
|
534
|
+
}
|
|
535
|
+
this.filterWindowOpen = true
|
|
536
|
+
},
|
|
537
|
+
setTimeseriesFilters: function(payload) {
|
|
538
|
+
this.$refs.viewerCanvas.setFilters(payload)
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
</script>
|
|
543
|
+
|
|
544
|
+
<style lang="scss" scoped>
|
|
545
|
+
.timeseries-viewer {
|
|
546
|
+
display: flex;
|
|
547
|
+
flex-direction: column;
|
|
548
|
+
}
|
|
549
|
+
#channelCanvas {
|
|
550
|
+
display: flex;
|
|
551
|
+
height:100%;
|
|
552
|
+
background-color: white;
|
|
553
|
+
//flex: 1;
|
|
554
|
+
}
|
|
555
|
+
#channelLabels {
|
|
556
|
+
display: flex;
|
|
557
|
+
flex-direction: column;
|
|
558
|
+
justify-content: space-around;
|
|
559
|
+
line-height: normal;
|
|
560
|
+
margin-bottom: 40px;
|
|
561
|
+
min-width: 75px;
|
|
562
|
+
}
|
|
563
|
+
.chLabelWrap {
|
|
564
|
+
display: flex;
|
|
565
|
+
flex-direction: column;
|
|
566
|
+
align-items: center;
|
|
567
|
+
cursor: pointer;
|
|
568
|
+
}
|
|
569
|
+
.chLabelIndWrap {
|
|
570
|
+
position: relative;
|
|
571
|
+
display: flex;
|
|
572
|
+
flex-direction: row;
|
|
573
|
+
justify-content: space-around;
|
|
574
|
+
width: 100%;
|
|
575
|
+
color: #3c54a4;
|
|
576
|
+
}
|
|
577
|
+
.chLabelInd {
|
|
578
|
+
font-size: 0.6em;
|
|
579
|
+
min-width: 70px;
|
|
580
|
+
color: rgb(150,150,150);
|
|
581
|
+
text-align: right;
|
|
582
|
+
white-space: nowrap;
|
|
583
|
+
}
|
|
584
|
+
.labelDiv[selected] {
|
|
585
|
+
color:#295eff; /*#ff9800;/*#358855;*/
|
|
586
|
+
}
|
|
587
|
+
.chLabelIndWrap[selected]{
|
|
588
|
+
color:#295eff; /*#ff9800; /*#358855;*/
|
|
589
|
+
}
|
|
590
|
+
.labelDiv {
|
|
591
|
+
align-self: flex-end;
|
|
592
|
+
white-space: nowrap;
|
|
593
|
+
color: var(--neuron);
|
|
594
|
+
}
|
|
595
|
+
</style>
|