mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-23 23:08:11 +08:00
* mocks for redux refactor - for discussion * more API design on redux refactor * add new reuqired dependencies for build * change babel target to use modern browser * remove dead code * remove dead code - joy plots * checkpoint on redux refactoring * checkpoint on redux refactoring * fix mistaken rebase conflict resolution * dead code removal; add name to dataframe backmap * rename dataframe to universe * update eslint config to more closely match prettier * lint * more eslint updates to match prettier * additional config to make eslint match prettier * add expression data to Universe/World * remove obsolete reducers * lint fixes * more eslint cleanup * lint * lint * fix but in countAllOnes when dimensions gt 1 * lint; do not display name metadata field * lint; colors refactor * lint; colors refactor * update comments * first cut at regraph and reset * enable object-curly-braces consistent mode * lint, handle regraph with no selection * fix expression scatterplot bugs * fix regression legend display * add expression data cache * remove console logging * reset cell color on regraph/reset * remove obsolete server URLs * rename UniverseV01 to Universe_REST_API_v01 * add additional comments on the varDataCache * merge universe reducer into controls reducer; simplify initialization-related actions * use spread operator * fix erroneous comment * convert universe and world state to plain objects, and functionalize supporting code (remove ES6 classes) * use spread operator * lint * improve variable names * rename obsCrossfilter to crossfilter and obsDimensionMap to dimensionMap * rename controls2 to controls
This commit is contained in:
@@ -1,138 +1,115 @@
|
||||
// jshint esversion: 6
|
||||
import uri from "urijs";
|
||||
import * as globals from "../globals";
|
||||
import _ from "lodash";
|
||||
import { parseRGB } from "../util/parseRGB";
|
||||
import * as d3 from "d3";
|
||||
import { interpolateViridis } from "d3-scale-chromatic";
|
||||
import * as globals from "../globals";
|
||||
import { parseRGB } from "../util/parseRGB";
|
||||
|
||||
/*
|
||||
https://medium.com/@jacobp100/you-arent-using-redux-middleware-enough-94ffe991e6
|
||||
storeInstance => functionToCallWithAnActionThatWillSendItToTheNextMiddleware => actionThatDispatchWasCalledWith => valueToUseAsTheReturnValueOfTheDispatchCall
|
||||
storeInstance =>
|
||||
functionToCallWithAnActionThatWillSendItToTheNextMiddleware =>
|
||||
actionThatDispatchWasCalledWith =>
|
||||
valueToUseAsTheReturnValueOfTheDispatchCall
|
||||
*/
|
||||
|
||||
/*
|
||||
What this file does:
|
||||
|
||||
1. fire a filter action anywhere in the app
|
||||
2. ** this middleware checks to see the state of all the currently selected filters, including the new one
|
||||
3. ** create updated selection from a copy of all the cells presently on the client (this may be a subset of 'all')
|
||||
4. ** append that new selection to the action so that it magically appears in the reducer just because the action was fired
|
||||
2. ** this middleware checks to see the state of all the currently selected filters,
|
||||
including the new one
|
||||
3. ** create updated selection from a copy of all the cells presently on the client
|
||||
(this may be a subset of 'all')
|
||||
4. ** append that new selection to the action so that it magically appears in the reducer
|
||||
just because the action was fired
|
||||
|
||||
This is nice because we keep a lot of filtering business logic centralized (what it means in practice to be selected)
|
||||
This is nice because we keep a lot of filtering business logic centralized
|
||||
(what it means in practice to be selected)
|
||||
*/
|
||||
|
||||
const updateCellColorsMiddleware = store => {
|
||||
return next => {
|
||||
return action => {
|
||||
const s = store.getState();
|
||||
const updateCellColorsMiddleware = store => next => action => {
|
||||
const s = store.getState();
|
||||
|
||||
/* this is a hardcoded map of the things we need to keep an eye on and update global cell selection in response to */
|
||||
const filterJustChanged =
|
||||
action.type === "color by expression" ||
|
||||
action.type === "color by continuous metadata" ||
|
||||
action.type === "color by categorical metadata";
|
||||
/*
|
||||
this is a hardcoded map of the things we need to keep an eye on and update
|
||||
global cell selection in response to
|
||||
*/
|
||||
const filterJustChanged =
|
||||
action.type === "color by expression" ||
|
||||
action.type === "color by continuous metadata" ||
|
||||
action.type === "color by categorical metadata";
|
||||
|
||||
if (!filterJustChanged || !s.controls.cellsMetadata) {
|
||||
return next(
|
||||
action
|
||||
); /* if the cells haven't loaded or the action wasn't a color change, bail */
|
||||
}
|
||||
if (!filterJustChanged || !s.controls.world.obsAnnotations) {
|
||||
return next(
|
||||
action
|
||||
); /* if the cells haven't loaded or the action wasn't a color change, bail */
|
||||
}
|
||||
|
||||
let cellsMetadataWithUpdatedColors = s.controls.cellsMetadata.slice(0);
|
||||
let colorScale;
|
||||
const { obsAnnotations } = s.controls.world;
|
||||
let colorScale;
|
||||
const colorsByName = new Array(obsAnnotations.length);
|
||||
const colorsByRGB = new Array(obsAnnotations.length);
|
||||
|
||||
/*
|
||||
in plain language...
|
||||
/*
|
||||
in plain language...
|
||||
(a) once the cells have loaded.
|
||||
(b) each time a user changes a color control we need to update cellsMetadata colors
|
||||
This is available to all the draw functions as world.colorName[index] or world.colorRGB[index]
|
||||
*/
|
||||
|
||||
(a) once the cells have loaded.
|
||||
(b) each time a user changes a color control we need to update cellsMetadata colors
|
||||
if (action.type === "color by categorical metadata") {
|
||||
colorScale = d3.scaleOrdinal().range(globals.ordinalColors);
|
||||
|
||||
This is available to all the draw functions as cell["__color__"] and cell["__colorRGB__"]
|
||||
*/
|
||||
for (let i = 0; i < obsAnnotations.length; i += 1) {
|
||||
const obs = obsAnnotations[i];
|
||||
const c = colorScale(obs[action.colorAccessor]);
|
||||
colorsByName[i] = c;
|
||||
colorsByRGB[i] = parseRGB(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (action.type === "color by categorical metadata") {
|
||||
colorScale = d3.scaleOrdinal().range(globals.ordinalColors);
|
||||
if (action.type === "color by continuous metadata") {
|
||||
colorScale = d3
|
||||
.scaleLinear()
|
||||
.domain([0, action.rangeMaxForColorAccessor])
|
||||
.range([1, 0]);
|
||||
|
||||
for (let i = 0; i < cellsMetadataWithUpdatedColors.length; i++) {
|
||||
const cell = cellsMetadataWithUpdatedColors[i];
|
||||
let c = colorScale(cell[action.colorAccessor]);
|
||||
cell.__color__ = c;
|
||||
cell.__colorRGB__ = parseRGB(c);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < obsAnnotations.length; i += 1) {
|
||||
const obs = obsAnnotations[i];
|
||||
const c = interpolateViridis(colorScale(obs[action.colorAccessor]));
|
||||
colorsByName[i] = c;
|
||||
colorsByRGB[i] = parseRGB(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (action.type === "color by continuous metadata") {
|
||||
colorScale = d3
|
||||
.scaleLinear()
|
||||
.domain([0, action.rangeMaxForColorAccessor])
|
||||
.range([1, 0]);
|
||||
if (action.type === "color by expression") {
|
||||
const { gene, data } = action;
|
||||
const expression = data[gene]; // Float32Array
|
||||
colorScale = d3
|
||||
.scaleLinear()
|
||||
.domain([_.min(expression), _.max(expression)])
|
||||
.range([
|
||||
1,
|
||||
0
|
||||
]); /* invert viridis... probably pass this scale through to others */
|
||||
|
||||
_.each(cellsMetadataWithUpdatedColors, (cell, i) => {
|
||||
let c = interpolateViridis(colorScale(cell[action.colorAccessor]));
|
||||
cellsMetadataWithUpdatedColors[i]["__color__"] = c;
|
||||
cellsMetadataWithUpdatedColors[i]["__colorRGB__"] = parseRGB(c);
|
||||
});
|
||||
}
|
||||
for (let i = 0, len = expression.length; i < len; i += 1) {
|
||||
const c = interpolateViridis(colorScale(expression[i]));
|
||||
colorsByName[i] = c;
|
||||
colorsByRGB[i] = parseRGB(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (action.type === "color by expression") {
|
||||
const indexOfGene = 0; /* we only get one, this comes from server as needed now */
|
||||
/*
|
||||
append the result of all the filters to the action the user just triggered
|
||||
*/
|
||||
const modifiedAction = Object.assign({}, action, {
|
||||
colors: { name: colorsByName, rgb: colorsByRGB },
|
||||
colorScale
|
||||
});
|
||||
|
||||
const expressionMap = {};
|
||||
/*
|
||||
converts [{cellname: cell123, e}, {}]
|
||||
|
||||
expressionMap = {
|
||||
cell123: [123, 2],
|
||||
cell789: [0, 8]
|
||||
}
|
||||
*/
|
||||
_.each(action.data.data.cells, cell => {
|
||||
/* this action is coming directly from the server */
|
||||
expressionMap[cell.cellname] = cell.e;
|
||||
});
|
||||
|
||||
const minExpressionCell = _.minBy(action.data.data.cells, cell => {
|
||||
return cell.e[indexOfGene];
|
||||
});
|
||||
|
||||
const maxExpressionCell = _.maxBy(action.data.data.cells, cell => {
|
||||
return cell.e[indexOfGene];
|
||||
});
|
||||
|
||||
// console.log('middle', action, expressionMap, minExpressionCell)
|
||||
|
||||
colorScale = d3
|
||||
.scaleLinear()
|
||||
.domain([
|
||||
minExpressionCell.e[indexOfGene],
|
||||
maxExpressionCell.e[indexOfGene]
|
||||
])
|
||||
.range([
|
||||
1,
|
||||
0
|
||||
]); /* invert viridis... probably pass this scale through to others */
|
||||
|
||||
_.each(cellsMetadataWithUpdatedColors, (cell, i) => {
|
||||
let c = interpolateViridis(
|
||||
colorScale(expressionMap[cell.CellName][indexOfGene])
|
||||
);
|
||||
cellsMetadataWithUpdatedColors[i]["__color__"] = c;
|
||||
cellsMetadataWithUpdatedColors[i]["__colorRGB__"] = parseRGB(c);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
append the result of all the filters to the action the user just triggered
|
||||
*/
|
||||
let modifiedAction = Object.assign({}, action, {
|
||||
cellsMetadataWithUpdatedColors,
|
||||
colorScale
|
||||
});
|
||||
|
||||
return next(modifiedAction);
|
||||
};
|
||||
};
|
||||
return next(modifiedAction);
|
||||
};
|
||||
|
||||
export default updateCellColorsMiddleware;
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
// jshint esversion: 6
|
||||
import uri from "urijs";
|
||||
import * as globals from "../globals";
|
||||
|
||||
/*
|
||||
XXX: this file should be obsolete. We just need to complete the refactoring
|
||||
of parallel.js and it can be removed entirely.
|
||||
|
||||
It is currently not in use - the middleware constructor does not include include it
|
||||
*/
|
||||
|
||||
/*
|
||||
https://medium.com/@jacobp100/you-arent-using-redux-middleware-enough-94ffe991e6
|
||||
storeInstance => functionToCallWithAnActionThatWillSendItToTheNextMiddleware => actionThatDispatchWasCalledWith => valueToUseAsTheReturnValueOfTheDispatchCall
|
||||
*/
|
||||
|
||||
/*
|
||||
What this file does:
|
||||
|
||||
1. fire a filter action anywhere in the app
|
||||
2. ** this middleware checks to see the state of all the currently selected filters, including the new one
|
||||
3. ** create updated selection from a copy of all the cells presently on the client (this may be a subset of 'all')
|
||||
4. ** append that new selection to the action so that it magically appears in the reducer just because the action was fired
|
||||
|
||||
This is nice because we keep a lot of filtering business logic centralized (what it means in practice to be selected)
|
||||
*/
|
||||
|
||||
const updateCellSelectionMiddleware = store => {
|
||||
return next => {
|
||||
return action => {
|
||||
const s = store.getState();
|
||||
|
||||
/* this is a hardcoded map of the things we need to keep an eye on and update global cell selection in response to */
|
||||
const filterJustChanged =
|
||||
action.type === "continuous selection using parallel coords brushing" ||
|
||||
action.type === "continuous metadata histogram brush" ||
|
||||
action.type === "graph brush selection change" ||
|
||||
action.type === "graph brush deselect" ||
|
||||
action.type === "categorical metadata filter deselect" ||
|
||||
action.type === "categorical metadata filter select" ||
|
||||
action.type === "categorical metadata filter none of these" ||
|
||||
action.type === "categorical metadata filter all of these";
|
||||
|
||||
if (!filterJustChanged || !s.controls.cellsMetadata) {
|
||||
return next(
|
||||
action
|
||||
); /* if the cells haven't loaded or the action wasn't a filter, bail */
|
||||
}
|
||||
|
||||
/*
|
||||
- make a FRESH copy of all of the cells
|
||||
- metadata has cellname and index, and that's all we ever need to reference cell info
|
||||
*/
|
||||
let newSelection = s.controls.cellsMetadata.slice(0);
|
||||
// _.forEach(newSelection, cell => (cell.__selected__ = true));
|
||||
for (let i = 0; i < newSelection.length; i++) {
|
||||
newSelection[i].__selected__ = true;
|
||||
}
|
||||
|
||||
/*
|
||||
in plain language...
|
||||
|
||||
(a) once the cells have loaded.
|
||||
(b) each time a user changes ANY control we need to update cellsMetadata
|
||||
there are two states:
|
||||
|
||||
1. control state we already know about (state.foo)
|
||||
2. control states that override states we already know about (action.foo applied instead of state.foo)
|
||||
|
||||
*/
|
||||
|
||||
if (
|
||||
(action.type ===
|
||||
"continuous selection using parallel coords brushing" &&
|
||||
s.controls.continuousSelection) ||
|
||||
s.controls.continuousSelection
|
||||
) {
|
||||
_.each(newSelection, (cell, i) => {
|
||||
const cellExtentsAreWithinContinuousSelectionBounds = s.controls.continuousSelection.every(
|
||||
active => {
|
||||
// test if point is within extents for each active brush
|
||||
return active.dimension.type.within(
|
||||
cell[active.dimension.key],
|
||||
active.extent,
|
||||
active.dimension
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
if (!cellExtentsAreWithinContinuousSelectionBounds) {
|
||||
newSelection[i]["__selected__"] = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let modifiedAction = Object.assign({}, action, {
|
||||
newSelection
|
||||
}); /* append the result of all the filters to the action the user just triggered */
|
||||
|
||||
return next(modifiedAction);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export default updateCellSelectionMiddleware;
|
||||
Reference in New Issue
Block a user