9
9
'use strict' ;
10
10
11
11
var glslify = require ( 'glslify' ) ;
12
- var c = require ( './constants' ) ;
13
12
var vertexShaderSource = glslify ( './shaders/vertex.glsl' ) ;
14
13
var contextShaderSource = glslify ( './shaders/context_vertex.glsl' ) ;
15
14
var pickVertexShaderSource = glslify ( './shaders/pick_vertex.glsl' ) ;
16
15
var fragmentShaderSource = glslify ( './shaders/fragment.glsl' ) ;
17
16
18
- var depthLimitEpsilon = 1e-6 ; // don't change; otherwise near/far plane lines are lost
17
+ var Lib = require ( '../../lib' ) ;
18
+
19
+ // don't change; otherwise near/far plane lines are lost
20
+ var depthLimitEpsilon = 1e-6 ;
21
+ // just enough buffer for an extra bit at single-precision floating point
22
+ // which on [0, 1] is 6e-8 (1/2^24)
23
+ var filterEpsilon = 1e-7 ;
24
+
25
+ // precision of multiselect is the full range divided into this many parts
26
+ var maskHeight = 2048 ;
27
+
19
28
20
29
var gpuDimensionCount = 64 ;
21
30
var sectionVertexCount = 2 ;
@@ -206,6 +215,8 @@ module.exports = function(canvasGL, d) {
206
215
207
216
var regl = d . regl ;
208
217
218
+ var mask , maskTexture ;
219
+
209
220
var paletteTexture = regl . texture ( {
210
221
shape : [ 256 , 1 ] ,
211
222
format : 'rgba' ,
@@ -295,6 +306,7 @@ module.exports = function(canvasGL, d) {
295
306
hiD : regl . prop ( 'hiD' ) ,
296
307
palette : paletteTexture ,
297
308
mask : regl . prop ( 'maskTexture' ) ,
309
+ maskHeight : regl . prop ( 'maskHeight' ) ,
298
310
colorClamp : regl . prop ( 'colorClamp' )
299
311
} ,
300
312
offset : regl . prop ( 'offset' ) ,
@@ -310,30 +322,22 @@ module.exports = function(canvasGL, d) {
310
322
311
323
var previousAxisOrder = [ ] ;
312
324
313
- function makeItem ( i , ii , x , y , panelSizeX , canvasPanelSizeY , crossfilterDimensionIndex , I , leftmost , rightmost ) {
325
+ function makeItem ( i , ii , x , y , panelSizeX , canvasPanelSizeY , crossfilterDimensionIndex , I , leftmost , rightmost , constraints ) {
314
326
var loHi , abcd , d , index ;
315
327
var leftRight = [ i , ii ] ;
316
- var filterEpsilon = c . verticalPadding / canvasPanelSizeY ;
317
328
318
329
var dims = [ 0 , 1 ] . map ( function ( ) { return [ 0 , 1 , 2 , 3 ] . map ( function ( ) { return new Float32Array ( 16 ) ; } ) ; } ) ;
319
- var lims = [ 0 , 1 ] . map ( function ( ) { return [ 0 , 1 , 2 , 3 ] . map ( function ( ) { return new Float32Array ( 16 ) ; } ) ; } ) ;
320
330
321
331
for ( loHi = 0 ; loHi < 2 ; loHi ++ ) {
322
332
index = leftRight [ loHi ] ;
323
333
for ( abcd = 0 ; abcd < 4 ; abcd ++ ) {
324
334
for ( d = 0 ; d < 16 ; d ++ ) {
325
- var dimP = d + 16 * abcd ;
326
335
dims [ loHi ] [ abcd ] [ d ] = d + 16 * abcd === index ? 1 : 0 ;
327
- if ( ! context ) {
328
- lims [ loHi ] [ abcd ] [ d ] = ( valid ( d , 16 * abcd , panelCount ) ? initialDims [ dimP === 0 ? 0 : 1 + ( ( dimP - 1 ) % ( initialDims . length - 1 ) ) ] . brush . filter . getBounds ( ) [ loHi ] : loHi ) + ( 2 * loHi - 1 ) * filterEpsilon ;
329
- }
330
336
}
331
337
}
332
338
}
333
339
334
- var mask , maskTexture ;
335
-
336
- var vm = {
340
+ var vm = Lib . extendFlat ( {
337
341
key : crossfilterDimensionIndex ,
338
342
resolution : [ canvasWidth , canvasHeight ] ,
339
343
viewBoxPosition : [ x + overdrag , y ] ,
@@ -361,55 +365,86 @@ module.exports = function(canvasGL, d) {
361
365
viewportY : model . pad . b + model . layoutHeight * domain . y [ 0 ] ,
362
366
viewportWidth : canvasWidth ,
363
367
viewportHeight : canvasHeight
364
- } ;
368
+ } , constraints ) ;
365
369
366
- if ( ! context ) {
367
- mask = Array . apply ( null , new Array ( canvasHeight * channelCount ) ) . map ( function ( ) {
368
- return 255 ;
369
- } ) ;
370
- for ( var dimIndex = 0 ; dimIndex < dimensionCount ; dimIndex ++ ) {
371
- var bitIndex = dimIndex % bitsPerByte ;
372
- var byteIndex = ( dimIndex - bitIndex ) / bitsPerByte ;
373
- var bitMask = Math . pow ( 2 , bitIndex ) ;
374
- var dim = initialDims [ dimIndex ] ;
375
- var ranges = dim . brush . filter . get ( ) ;
376
- if ( ranges . length < 2 ) continue ; // bail if the bounding box based filter is sufficient
377
- var pi , pixelRange ;
378
- var boundingBox = dim . brush . filter . getBounds ( ) ;
379
- pixelRange = boundingBox . map ( dim . unitScaleInOrder ) ;
380
- for ( pi = Math . max ( 0 , Math . floor ( pixelRange [ 0 ] ) ) ; pi <= Math . min ( canvasHeight - 1 , Math . ceil ( pixelRange [ 1 ] ) ) ; pi ++ ) {
381
- mask [ pi * channelCount + byteIndex ] &= ~ bitMask ; // clear bits
382
- }
383
- for ( var r = 0 ; r < ranges . length ; r ++ ) {
384
- pixelRange = ranges [ r ] . map ( dim . unitScaleInOrder ) ;
385
- for ( pi = Math . max ( 0 , Math . floor ( pixelRange [ 0 ] ) ) ; pi <= Math . min ( canvasHeight - 1 , Math . ceil ( pixelRange [ 1 ] ) ) ; pi ++ ) {
386
- mask [ pi * channelCount + byteIndex ] |= bitMask ;
370
+ return vm ;
371
+ }
372
+
373
+ function makeConstraints ( ) {
374
+ var loHi , abcd , d ;
375
+
376
+ var lims = [ 0 , 1 ] . map ( function ( ) { return [ 0 , 1 , 2 , 3 ] . map ( function ( ) { return new Float32Array ( 16 ) ; } ) ; } ) ;
377
+
378
+ for ( loHi = 0 ; loHi < 2 ; loHi ++ ) {
379
+ for ( abcd = 0 ; abcd < 4 ; abcd ++ ) {
380
+ for ( d = 0 ; d < 16 ; d ++ ) {
381
+ var dimP = d + 16 * abcd ;
382
+ var lim ;
383
+ if ( valid ( d , 16 * abcd , panelCount ) ) {
384
+ var dimi = initialDims [ dimP === 0 ? 0 : 1 + ( ( dimP - 1 ) % ( initialDims . length - 1 ) ) ] ;
385
+ lim = dimi . brush . filter . getBounds ( ) [ loHi ] ;
387
386
}
387
+ else lim = loHi ;
388
+ lims [ loHi ] [ abcd ] [ d ] = lim + ( 2 * loHi - 1 ) * filterEpsilon ;
388
389
}
389
390
}
391
+ }
390
392
391
- maskTexture = regl . texture ( {
392
- shape : [ channelCount , canvasHeight ] , // 8 units x 8 bits = 64 bits, just sufficient for the almost 64 dimensions we support
393
- format : 'alpha' ,
394
- type : 'uint8' ,
395
- mag : 'nearest' ,
396
- min : 'nearest' ,
397
- data : mask
398
- } ) ;
393
+ var maskExpansion = maskHeight / canvasHeight ;
399
394
400
- vm . maskTexture = maskTexture ;
395
+ function expandedPixelRange ( dim , bounds ) {
396
+ var originalPixelRange = bounds . map ( dim . unitScaleInOrder ) ;
397
+ return [
398
+ Math . max ( 0 , Math . floor ( originalPixelRange [ 0 ] * maskExpansion ) ) ,
399
+ Math . min ( maskHeight - 1 , Math . ceil ( originalPixelRange [ 1 ] * maskExpansion ) )
400
+ ] ;
401
+ }
401
402
402
- vm . loA = lims [ 0 ] [ 0 ] ;
403
- vm . loB = lims [ 0 ] [ 1 ] ;
404
- vm . loC = lims [ 0 ] [ 2 ] ;
405
- vm . loD = lims [ 0 ] [ 3 ] ;
406
- vm . hiA = lims [ 1 ] [ 0 ] ;
407
- vm . hiB = lims [ 1 ] [ 1 ] ;
408
- vm . hiC = lims [ 1 ] [ 2 ] ;
409
- vm . hiD = lims [ 1 ] [ 3 ] ;
403
+ mask = Array . apply ( null , new Array ( maskHeight * channelCount ) ) . map ( function ( ) {
404
+ return 255 ;
405
+ } ) ;
406
+ for ( var dimIndex = 0 ; dimIndex < dimensionCount ; dimIndex ++ ) {
407
+ var bitIndex = dimIndex % bitsPerByte ;
408
+ var byteIndex = ( dimIndex - bitIndex ) / bitsPerByte ;
409
+ var bitMask = Math . pow ( 2 , bitIndex ) ;
410
+ var dim = initialDims [ dimIndex ] ;
411
+ var ranges = dim . brush . filter . get ( ) . sort ( function ( a , b ) { return a [ 0 ] - b [ 0 ] ; } ) ;
412
+ if ( ranges . length < 2 ) continue ; // bail if the bounding box based filter is sufficient
413
+
414
+ var prevEnd = expandedPixelRange ( dim , ranges [ 0 ] ) [ 1 ] ;
415
+ for ( var ri = 1 ; ri < ranges . length ; ri ++ ) {
416
+ var nextRange = expandedPixelRange ( dim , ranges [ ri ] ) ;
417
+ for ( var pi = prevEnd + 1 ; pi < nextRange [ 0 ] ; pi ++ ) {
418
+ mask [ pi * channelCount + byteIndex ] &= ~ bitMask ;
419
+ }
420
+ prevEnd = Math . max ( prevEnd , nextRange [ 1 ] ) ;
421
+ }
410
422
}
411
423
412
- return vm ;
424
+ var textureData = {
425
+ // 8 units x 8 bits = 64 bits, just sufficient for the almost 64 dimensions we support
426
+ shape : [ channelCount , maskHeight ] ,
427
+ format : 'alpha' ,
428
+ type : 'uint8' ,
429
+ mag : 'nearest' ,
430
+ min : 'nearest' ,
431
+ data : mask
432
+ } ;
433
+ if ( maskTexture ) maskTexture ( textureData ) ;
434
+ else maskTexture = regl . texture ( textureData ) ;
435
+
436
+ return {
437
+ maskTexture : maskTexture ,
438
+ maskHeight : maskHeight ,
439
+ loA : lims [ 0 ] [ 0 ] ,
440
+ loB : lims [ 0 ] [ 1 ] ,
441
+ loC : lims [ 0 ] [ 2 ] ,
442
+ loD : lims [ 0 ] [ 3 ] ,
443
+ hiA : lims [ 1 ] [ 0 ] ,
444
+ hiB : lims [ 1 ] [ 1 ] ,
445
+ hiC : lims [ 1 ] [ 2 ] ,
446
+ hiD : lims [ 1 ] [ 3 ]
447
+ } ;
413
448
}
414
449
415
450
function renderGLParcoords ( panels , setChanged , clearOnly ) {
@@ -433,6 +468,7 @@ module.exports = function(canvasGL, d) {
433
468
// clear canvas here, as the panel iteration below will not enter the loop body
434
469
clear ( regl , 0 , 0 , canvasWidth , canvasHeight ) ;
435
470
}
471
+ var constraints = context ? { } : makeConstraints ( ) ;
436
472
437
473
for ( I = 0 ; I < panelCount ; I ++ ) {
438
474
var panel = panels [ I ] ;
@@ -447,7 +483,7 @@ module.exports = function(canvasGL, d) {
447
483
var xTo = x + panelSizeX ;
448
484
if ( setChanged || ! previousAxisOrder [ i ] || previousAxisOrder [ i ] [ 0 ] !== x || previousAxisOrder [ i ] [ 1 ] !== xTo ) {
449
485
previousAxisOrder [ i ] = [ x , xTo ] ;
450
- var item = makeItem ( i , ii , x , y , panelSizeX , panelSizeY , dim1 . crossfilterDimensionIndex , I , leftmost , rightmost ) ;
486
+ var item = makeItem ( i , ii , x , y , panelSizeX , panelSizeY , dim1 . crossfilterDimensionIndex , I , leftmost , rightmost , constraints ) ;
451
487
renderState . clearOnly = clearOnly ;
452
488
renderBlock ( regl , glAes , renderState , setChanged ? lines . blockLineCount : sampleCount , sampleCount , item ) ;
453
489
}
0 commit comments