@@ -12,6 +12,7 @@ var c = require('./constants');
12
12
var d3 = require ( 'd3' ) ;
13
13
var keyFun = require ( '../../lib/gup' ) . keyFun ;
14
14
var repeat = require ( '../../lib/gup' ) . repeat ;
15
+ var sortAsc = require ( '../../lib' ) . sorterAsc ;
15
16
16
17
function addFilterBarDefs ( defs ) {
17
18
var filterBarPattern = defs . selectAll ( '#' + c . id . filterBarPattern )
@@ -115,8 +116,7 @@ function setHighlight(d) {
115
116
if ( ! filterActive ( d . brush ) ) {
116
117
return '0 ' + d . height ;
117
118
}
118
- var unitRanges = d . brush . filter . getConsolidated ( ) ;
119
- var pixelRanges = unitRanges . map ( function ( pr ) { return pr . map ( d . unitScaleInOrder ) ; } ) ;
119
+ var pixelRanges = unitToPx ( d . brush . filter . getConsolidated ( ) , d . height ) ;
120
120
var dashArray = [ 0 ] ; // we start with a 0 length selection as filter ranges are inclusive, not exclusive
121
121
var p , sectionHeight , iNext ;
122
122
var currentGap = pixelRanges . length ? pixelRanges [ 0 ] [ 0 ] : null ;
@@ -138,6 +138,12 @@ function setHighlight(d) {
138
138
return dashArray ;
139
139
}
140
140
141
+ function unitToPx ( unitRanges , height ) {
142
+ return unitRanges . map ( function ( pr ) {
143
+ return pr . map ( function ( v ) { return v * height ; } ) . sort ( sortAsc ) ;
144
+ } ) ;
145
+ }
146
+
141
147
function differentInterval ( int1 ) {
142
148
// An interval is different if the extents don't match, which is a safe test only because the intervals
143
149
// get consolidated anyway (ie. the identity of overlapping intervals won't be preserved; they get fused)
@@ -187,9 +193,9 @@ function renderHighlight(root, tweenCallback) {
187
193
styleHighlight ( barToStyle ) ;
188
194
}
189
195
190
- function getInterval ( b , unitScaleInOrder , y ) {
196
+ function getInterval ( b , height , y ) {
191
197
var intervals = b . filter . getConsolidated ( ) ;
192
- var pixIntervals = intervals . map ( function ( interval ) { return interval . map ( unitScaleInOrder ) ; } ) ;
198
+ var pixIntervals = unitToPx ( intervals , height ) ;
193
199
var hoveredInterval = NaN ;
194
200
var previousInterval = NaN ;
195
201
var nextInterval = NaN ;
@@ -243,8 +249,8 @@ function attachDragBehavior(selection) {
243
249
if ( d . parent . inBrushDrag ) {
244
250
return ;
245
251
}
246
- var y = d . unitScaleInOrder ( d . unitScale . invert ( d3 . mouse ( this ) [ 1 ] + c . verticalPadding ) ) ;
247
- var interval = getInterval ( b , d . unitScaleInOrder , y ) ;
252
+ var y = d . height - d3 . mouse ( this ) [ 1 ] - 2 * c . verticalPadding ;
253
+ var interval = getInterval ( b , d . height , y ) ;
248
254
d3 . select ( document . body )
249
255
. style ( 'cursor' , interval . n ? 'n-resize' : interval . s ? 's-resize' : ! interval . m ? 'crosshair' : filterActive ( b ) ? 'ns-resize' : 'crosshair' ) ;
250
256
} )
@@ -258,10 +264,10 @@ function attachDragBehavior(selection) {
258
264
. on ( 'dragstart' , function ( d ) {
259
265
var e = d3 . event ;
260
266
e . sourceEvent . stopPropagation ( ) ;
261
- var y = d . unitScaleInOrder ( d . unitScale . invert ( d3 . mouse ( this ) [ 1 ] + c . verticalPadding ) ) ;
267
+ var y = d . height - d3 . mouse ( this ) [ 1 ] - 2 * c . verticalPadding ;
262
268
var unitLocation = d . unitScaleInOrder . invert ( y ) ;
263
269
var b = d . brush ;
264
- var intData = getInterval ( b , d . unitScaleInOrder , y ) ;
270
+ var intData = getInterval ( b , d . height , y ) ;
265
271
var unitRange = intData . interval ;
266
272
var pixelRange = unitRange . map ( d . unitScaleInOrder ) ;
267
273
var s = b . svgBrush ;
@@ -346,13 +352,16 @@ function attachDragBehavior(selection) {
346
352
s . brushEndCallback ( filter . get ( ) ) ;
347
353
return ; // no need to fuse intervals or snap to ordinals, so we can bail early
348
354
}
355
+
349
356
var mergeIntervals = function ( ) {
350
357
// Key piece of logic: once the button is released, possibly overlapping intervals will be fused:
351
358
// Here it's done immediately on click release while on ordinal snap transition it's done at the end
352
359
filter . set ( filter . getConsolidated ( ) ) ;
353
360
} ;
361
+
354
362
if ( d . ordinal ) {
355
- var a = d . ordinalScale . range ( ) ;
363
+ var a = d . paddedUnitValues ;
364
+ if ( a [ a . length - 1 ] < a [ 0 ] ) a = a . slice ( ) . sort ( sortAsc ) ;
356
365
s . newExtent = [
357
366
ordinalScaleSnapLo ( a , s . newExtent [ 0 ] , s . stayingIntervals ) ,
358
367
ordinalScaleSnapHi ( a , s . newExtent [ 1 ] , s . stayingIntervals )
@@ -366,11 +375,13 @@ function attachDragBehavior(selection) {
366
375
} else {
367
376
mergeIntervals ( ) ; // merging intervals immediately
368
377
}
369
- s . brushEndCallback ( filter . get ( ) ) ;
378
+ s . brushEndCallback ( filter . getConsolidated ( ) ) ;
370
379
} )
371
380
) ;
372
381
}
373
382
383
+ function startAsc ( a , b ) { return a [ 0 ] - b [ 0 ] ; }
384
+
374
385
function renderAxisBrush ( axisBrush ) {
375
386
376
387
var background = axisBrush . selectAll ( '.background' ) . data ( repeat ) ;
@@ -458,7 +469,7 @@ function axisBrushMoved(callback) {
458
469
function dedupeRealRanges ( intervals ) {
459
470
// Fuses elements of intervals if they overlap, yielding discontiguous intervals, results.length <= intervals.length
460
471
// Currently uses closed intervals, ie. dedupeRealRanges([[400, 800], [300, 400]]) -> [300, 800]
461
- var queue = intervals . slice ( ) . sort ( function ( a , b ) { return a [ 0 ] - b [ 0 ] ; } ) ; // ordered by interval start
472
+ var queue = intervals . slice ( ) ;
462
473
var result = [ ] ;
463
474
var currentInterval ;
464
475
var current = queue . shift ( ) ;
@@ -475,14 +486,20 @@ function dedupeRealRanges(intervals) {
475
486
function makeFilter ( ) {
476
487
var filter = [ ] ;
477
488
var consolidated ;
489
+ var bounds ;
478
490
return {
479
491
set : function ( a ) {
480
- filter = a . slice ( ) . map ( function ( d ) { return d . slice ( ) ; } ) ;
481
- consolidated = dedupeRealRanges ( a ) ;
492
+ filter = a
493
+ . map ( function ( d ) { return d . slice ( ) . sort ( sortAsc ) ; } )
494
+ . sort ( startAsc ) ;
495
+ consolidated = dedupeRealRanges ( filter ) ;
496
+ bounds = filter . reduce ( function ( p , n ) {
497
+ return [ Math . min ( p [ 0 ] , n [ 0 ] ) , Math . max ( p [ 1 ] , n [ 1 ] ) ] ;
498
+ } , [ Infinity , - Infinity ] ) ;
482
499
} ,
483
- get : function ( ) { return filter . slice ( ) ; } ,
484
- getConsolidated : function ( ) { return consolidated ; } , // would be nice if slow to slice in two layers...
485
- getBounds : function ( ) { return filter . reduce ( function ( p , n ) { return [ Math . min ( p [ 0 ] , n [ 0 ] ) , Math . max ( p [ 1 ] , n [ 1 ] ) ] ; } , [ Infinity , - Infinity ] ) ; }
500
+ get : function ( ) { return filter . slice ( ) ; } ,
501
+ getConsolidated : function ( ) { return consolidated ; } ,
502
+ getBounds : function ( ) { return bounds ; }
486
503
} ;
487
504
}
488
505
@@ -501,9 +518,38 @@ function makeBrush(state, rangeSpecified, initialRange, brushStartCallback, brus
501
518
} ;
502
519
}
503
520
521
+ // for use by supplyDefaults, but it needed tons of pieces from here so
522
+ // seemed to make more sense just to put the whole routine here
523
+ function cleanRanges ( ranges , dimension ) {
524
+ if ( Array . isArray ( ranges [ 0 ] ) ) {
525
+ ranges = ranges . map ( function ( ri ) { return ri . sort ( sortAsc ) ; } ) ;
526
+
527
+ if ( ! dimension . multiselect ) ranges = [ ranges [ 0 ] ] ;
528
+ else ranges = dedupeRealRanges ( ranges . sort ( startAsc ) ) ;
529
+ }
530
+ else ranges = [ ranges . sort ( sortAsc ) ] ;
531
+
532
+ // ordinal snapping
533
+ if ( dimension . tickvals ) {
534
+ var sortedTickVals = dimension . tickvals . slice ( ) . sort ( sortAsc ) ;
535
+ ranges = ranges . map ( function ( ri ) {
536
+ var rSnapped = [
537
+ ordinalScaleSnapLo ( sortedTickVals , ri [ 0 ] , [ ] ) ,
538
+ ordinalScaleSnapHi ( sortedTickVals , ri [ 1 ] , [ ] )
539
+ ] ;
540
+ if ( rSnapped [ 1 ] > rSnapped [ 0 ] ) return rSnapped ;
541
+ } )
542
+ . filter ( function ( ri ) { return ri ; } ) ;
543
+
544
+ if ( ! ranges . length ) return ;
545
+ }
546
+ return ranges . length > 1 ? ranges : ranges [ 0 ] ;
547
+ }
548
+
504
549
module . exports = {
505
550
addFilterBarDefs : addFilterBarDefs ,
506
551
makeBrush : makeBrush ,
507
552
ensureAxisBrush : ensureAxisBrush ,
508
- filterActive : filterActive
553
+ filterActive : filterActive ,
554
+ cleanRanges : cleanRanges
509
555
} ;
0 commit comments