in src/parallel/parallel.tsx [468:602]
function brush() {
/**
* Called whenever a brush happens. Recomputes which points are selected.
*/
if (me.props.context_menu_ref !== undefined) {
me.props.context_menu_ref.current.hide();
}
var extents = brush_extends();
var actives = me.state.dimensions.filter(function(p) { return extents[p] !== null && extents[p] !== undefined; });
// hack to hide ticks beyond extent
me.dimensions_dom
.each(function(dimension) {
if (_.include(actives, dimension)) {
var scale = me.yscale[dimension];
var extent = extents[dimension];
d3.select(this)
.selectAll('text')
.classed(style.tickSelected, true)
.style('display', function() {
if (d3.select(this).classed(style.label)) {
return null;
}
var value = d3.select(this).data();
return extent[0] <= scale(value) && scale(value) <= extent[1] ? null : "none";
});
} else {
d3.select(this)
.selectAll('text')
.classed(style.tickSelected, false)
.style('display', null);
}
d3.select(this)
.selectAll("." + style.label)
.style('display', null);
});
;
// bold dimensions with label
div.selectAll("." + style.label)
.style("font-weight", function(dimension) {
if (_.include(actives, dimension)) return "bold";
return null;
});
// Get lines within extents
var filters: Array<Filter> = actives.map(function(dimension) {
const scale = me.yscale[dimension];
var extent = extents[dimension];
const range = scale_pixels_range(scale, extent);
if (range.type == ParamType.CATEGORICAL && !range.values) {
// Select nothing
return {
type: FilterType.Not,
data: {
type: FilterType.All,
data: [],
}
};
}
var min, max;
if (range.type == ParamType.CATEGORICAL) {
if (range.values.length == 0) {
return {
type: FilterType.None,
data: {}
};
}
min = range.values[0];
max = range.values[range.values.length - 1];
console.assert(typeof min == typeof max, min, max);
}
else {
min = Math.min(...range.range);
max = Math.max(...range.range);
}
return {
type: FilterType.Range,
data: {
col: dimension,
type: range.type,
min: min,
max: max,
include_infnans: range.include_infnans,
}
};
});
const selected = apply_filters(me.props.rows_filtered, filters);
if (me.props.asserts) {
// Check that pixel-based selected rows
// match filters-based selected rows
// But relax the verification a bit - math errors can happen
// and we only require a 1 pixel precision
var selected_pixels_minset = [];
var selected_pixels_maxset = [];
me.props.rows_filtered
.forEach(function(d) {
if (actives.every(function(dimension) {
var scale = me.yscale[dimension];
var extent = extents[dimension];
var value = d[dimension];
return extent[0] + 1 <= scale(value) && scale(value) <= extent[1] - 1;
})) {
selected_pixels_minset.push(d);
}
if (actives.every(function(dimension) {
var scale = me.yscale[dimension];
var extent = extents[dimension];
var value = d[dimension];
return extent[0] - 1 <= scale(value) && scale(value) <= extent[1] + 1;
})) {
selected_pixels_maxset.push(d);
}
});
const missed = _.difference(selected_pixels_minset, selected);
const overselected = _.difference(selected, selected_pixels_maxset);
if (overselected.length || missed.length) {
console.error(`Warning! Filter on ${actives.join(" ")} (`, filters, ") does not match actually selected rows",
" Computed rows with filter:", selected,
" Missed:", missed, " Falsely selected:", overselected);
console.error("filters", filters, JSON.stringify(filters));
if (missed.length) {
console.error("first missed", JSON.stringify(missed[0]));
}
if (overselected.length) {
console.error("first falsely selected", JSON.stringify(overselected[0]));
}
}
}
me.props.setSelected(selected, {
type: FilterType.All,
data: filters,
});
}