@@ -15,7 +15,7 @@ var axisIds = require('../plots/cartesian/axis_ids');
1515var autoType = require ( '../plots/cartesian/axis_autotype' ) ;
1616var setConvert = require ( '../plots/cartesian/set_convert' ) ;
1717
18- var INEQUALITY_OPS = [ '=' , '<' , '>=' , '>' , '<=' ] ;
18+ var COMPARISON_OPS = [ '=' , '! =', '<' , '>=' , '>' , '<=' ] ;
1919var INTERVAL_OPS = [ '[]' , '()' , '[)' , '(]' , '][' , ')(' , '](' , ')[' ] ;
2020var SET_OPS = [ '{}' , '}{' ] ;
2121
@@ -51,12 +51,16 @@ exports.attributes = {
5151 } ,
5252 operation : {
5353 valType : 'enumerated' ,
54- values : [ ] . concat ( INEQUALITY_OPS ) . concat ( INTERVAL_OPS ) . concat ( SET_OPS ) ,
54+ values : [ ]
55+ . concat ( COMPARISON_OPS )
56+ . concat ( INTERVAL_OPS )
57+ . concat ( SET_OPS ) ,
5558 dflt : '=' ,
5659 description : [
5760 'Sets the filter operation.' ,
5861
5962 '*=* keeps items equal to `value`' ,
63+ '*!=* keeps items not equal to `value`' ,
6064
6165 '*<* keeps items less than `value`' ,
6266 '*<=* keeps items less than or equal to `value`' ,
@@ -87,21 +91,31 @@ exports.attributes = {
8791 'Values are expected to be in the same type as the data linked' ,
8892 'to *target*.' ,
8993
90- 'When `operation` is set to one of the inequality values ' ,
91- '(' + INEQUALITY_OPS + ')' ,
94+ 'When `operation` is set to one of' ,
95+ 'the comparison values (' + COMPARISON_OPS + ')' ,
9296 '*value* is expected to be a number or a string.' ,
9397
94- 'When `operation` is set to one of the interval value ' ,
98+ 'When `operation` is set to one of the interval values ' ,
9599 '(' + INTERVAL_OPS + ')' ,
96100 '*value* is expected to be 2-item array where the first item' ,
97101 'is the lower bound and the second item is the upper bound.' ,
98102
99- 'When `operation`, is set to one of the set value ' ,
103+ 'When `operation`, is set to one of the set values ' ,
100104 '(' + SET_OPS + ')' ,
101105 '*value* is expected to be an array with as many items as' ,
102106 'the desired set elements.'
103107 ] . join ( ' ' )
104- }
108+ } ,
109+ preservegaps : {
110+ valType : 'boolean' ,
111+ dflt : false ,
112+ description : [
113+ 'Determines whether or not gaps in data arrays produced by the filter operation' ,
114+ 'are preserved.' ,
115+ 'Setting this to *true* might be useful when plotting a line chart' ,
116+ 'with `connectgaps` set to *false*.'
117+ ] . join ( ' ' )
118+ } ,
105119} ;
106120
107121exports . supplyDefaults = function ( transformIn ) {
@@ -114,6 +128,7 @@ exports.supplyDefaults = function(transformIn) {
114128 var enabled = coerce ( 'enabled' ) ;
115129
116130 if ( enabled ) {
131+ coerce ( 'preservegaps' ) ;
117132 coerce ( 'operation' ) ;
118133 coerce ( 'value' ) ;
119134 coerce ( 'target' ) ;
@@ -149,36 +164,47 @@ exports.calcTransform = function(gd, trace, opts) {
149164 var d2cTarget = ( target === 'x' || target === 'y' || target === 'z' ) ?
150165 target : filterArray ;
151166
152- var dataToCoord = getDataToCoordFunc ( gd , trace , d2cTarget ) ,
153- filterFunc = getFilterFunc ( opts , dataToCoord , targetCalendar ) ,
154- arrayAttrs = PlotSchema . findArrayAttributes ( trace ) ,
155- originalArrays = { } ;
156-
157- // copy all original array attribute values,
158- // and clear arrays in trace
159- for ( var k = 0 ; k < arrayAttrs . length ; k ++ ) {
160- var attr = arrayAttrs [ k ] ,
161- np = Lib . nestedProperty ( trace , attr ) ;
167+ var dataToCoord = getDataToCoordFunc ( gd , trace , d2cTarget ) ;
168+ var filterFunc = getFilterFunc ( opts , dataToCoord , targetCalendar ) ;
169+ var arrayAttrs = PlotSchema . findArrayAttributes ( trace ) ;
170+ var originalArrays = { } ;
162171
163- originalArrays [ attr ] = Lib . extendDeep ( [ ] , np . get ( ) ) ;
164- np . set ( [ ] ) ;
172+ function forAllAttrs ( fn , index ) {
173+ for ( var j = 0 ; j < arrayAttrs . length ; j ++ ) {
174+ var np = Lib . nestedProperty ( trace , arrayAttrs [ j ] ) ;
175+ fn ( np , index ) ;
176+ }
165177 }
166178
167- function fill ( attr , i ) {
168- var oldArr = originalArrays [ attr ] ,
169- newArr = Lib . nestedProperty ( trace , attr ) . get ( ) ;
170-
171- newArr . push ( oldArr [ i ] ) ;
179+ var initFn ;
180+ var fillFn ;
181+ if ( opts . preservegaps ) {
182+ initFn = function ( np ) {
183+ originalArrays [ np . astr ] = Lib . extendDeep ( [ ] , np . get ( ) ) ;
184+ np . set ( new Array ( len ) ) ;
185+ } ;
186+ fillFn = function ( np , index ) {
187+ var val = originalArrays [ np . astr ] [ index ] ;
188+ np . get ( ) [ index ] = val ;
189+ } ;
190+ } else {
191+ initFn = function ( np ) {
192+ originalArrays [ np . astr ] = Lib . extendDeep ( [ ] , np . get ( ) ) ;
193+ np . set ( [ ] ) ;
194+ } ;
195+ fillFn = function ( np , index ) {
196+ var val = originalArrays [ np . astr ] [ index ] ;
197+ np . get ( ) . push ( val ) ;
198+ } ;
172199 }
173200
174- for ( var i = 0 ; i < len ; i ++ ) {
175- var v = filterArray [ i ] ;
176-
177- if ( ! filterFunc ( v ) ) continue ;
201+ // copy all original array attribute values, and clear arrays in trace
202+ forAllAttrs ( initFn ) ;
178203
179- for ( var j = 0 ; j < arrayAttrs . length ; j ++ ) {
180- fill ( arrayAttrs [ j ] , i ) ;
181- }
204+ // loop through filter array, fill trace arrays if passed
205+ for ( var i = 0 ; i < len ; i ++ ) {
206+ var passed = filterFunc ( filterArray [ i ] ) ;
207+ if ( passed ) forAllAttrs ( fillFn , i ) ;
182208 }
183209} ;
184210
@@ -245,7 +271,7 @@ function getFilterFunc(opts, d2c, targetCalendar) {
245271
246272 var coercedValue ;
247273
248- if ( isOperationIn ( INEQUALITY_OPS ) ) {
274+ if ( isOperationIn ( COMPARISON_OPS ) ) {
249275 coercedValue = hasArrayValue ? d2cValue ( value [ 0 ] ) : d2cValue ( value ) ;
250276 }
251277 else if ( isOperationIn ( INTERVAL_OPS ) ) {
@@ -262,6 +288,9 @@ function getFilterFunc(opts, d2c, targetCalendar) {
262288 case '=' :
263289 return function ( v ) { return d2cTarget ( v ) === coercedValue ; } ;
264290
291+ case '!=' :
292+ return function ( v ) { return d2cTarget ( v ) !== coercedValue ; } ;
293+
265294 case '<' :
266295 return function ( v ) { return d2cTarget ( v ) < coercedValue ; } ;
267296
0 commit comments