-
Notifications
You must be signed in to change notification settings - Fork 163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Convey uncertainty via tip colors #1796
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -68,35 +68,54 @@ export const calcNodeColor = (tree, colorScale) => { | |
// scale entropy such that higher entropy maps to a grayer less-certain branch | ||
const branchInterpolateColour = "#BBB"; | ||
const branchOpacityConstant = 0.6; | ||
export const branchOpacityFunction = scalePow() | ||
const branchOpacityFunction = scalePow() | ||
.exponent([0.6]) | ||
.domain([0, 2.0]) | ||
.range([0.4, 1]) | ||
.domain([0, 2.0]) // entropy values close to 0 -> ~100% confidence, close to 2 -> very little confidence | ||
.range([0.4, 1]) // 0 -> return original node colour, 1 -> return branchInterpolateColour | ||
.clamp(true); | ||
const tipOpacityFunction = branchOpacityFunction | ||
.copy() | ||
.range([0, 0.9]); // if entropy close to 0 return the original node color | ||
|
||
|
||
// entropy calculation precomputed in augur | ||
// export const calcEntropyOfValues = (vals) => | ||
// vals.map((v) => v * Math.log(v + 1E-10)).reduce((a, b) => a + b, 0) * -1 / Math.log(vals.length); | ||
|
||
/** | ||
* calculate array of HEXs to actually be displayed. | ||
* (colorBy) confidences manifest as opacity ramps | ||
* Calculate an array of stroke colors to render for a branch or tip node. These are "grey-er" versions | ||
* of the underlying `tree.nodeColours`. The degree of grey-ness is obtained via interpolation | ||
* between the node color and `branchOpacityConstant`. The interpolation parameter varies | ||
* depending on the confidence we have in the trait (the entropy), with more confidence resulting | ||
* in more saturated colours. For branches we always make them slightly greyer (even in the absence | ||
* of uncertainty) for purely aesthetic reasons. | ||
* @param {obj} tree phyloTree object | ||
* @param {bool} branch will this color be used for the branch or the tip? | ||
* @param {bool} confidence enabled? | ||
* @return {array} array of hex's. 1-1 with nodes. | ||
*/ | ||
export const calcBranchStrokeCols = (tree, confidence, colorBy) => { | ||
export const calculateStrokeColors = (tree, branch, confidence, colorBy) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not a request for change, just curious Why combine branch/tip colors into one function with the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While they are ~separate code paths, they both do the same thing: take a colour and modify it according to the node's uncertainty. Co-locating them feels natural to me and should help them to stay in-sync. I actually thought about taking this further and refactoring it into a function |
||
if (confidence === true) { | ||
return tree.nodeColors.map((col, idx) => { | ||
const entropy = getTraitFromNode(tree.nodes[idx], colorBy, {entropy: true}); | ||
const opacity = entropy ? branchOpacityFunction(entropy) : branchOpacityConstant; | ||
return rgb(interpolateRgb(col, branchInterpolateColour)(opacity)).toString(); | ||
}); | ||
return tree.nodeColors.map(branch ? _confidenceBranchColor : _confidenceTipColor) | ||
} | ||
return branch ? tree.nodeColors.map(_defaultBranchColor) : tree.nodeColors; | ||
|
||
function _confidenceBranchColor(col, idx) { | ||
const entropy = getTraitFromNode(tree.nodes[idx], colorBy, {entropy: true}); | ||
if (!entropy) return _defaultBranchColor(col); | ||
return rgb(interpolateRgb(col, branchInterpolateColour)(branchOpacityFunction(entropy))).toString(); | ||
} | ||
|
||
function _confidenceTipColor(col, idx) { | ||
if (tree.nodes[idx].hasChildren) return undefined; // skip computation for internal nodes | ||
const entropy = getTraitFromNode(tree.nodes[idx], colorBy, {entropy: true}); | ||
if (!entropy) return col; | ||
return rgb(interpolateRgb(col, branchInterpolateColour)(tipOpacityFunction(entropy))).toString(); | ||
} | ||
|
||
function _defaultBranchColor(col) { | ||
return rgb(interpolateRgb(col, branchInterpolateColour)(branchOpacityConstant)).toString() | ||
} | ||
return tree.nodeColors.map((col) => { | ||
return rgb(interpolateRgb(col, branchInterpolateColour)(branchOpacityConstant)).toString(); | ||
}); | ||
}; | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The range (and the domain) of this scale are the magic values which control how entropy values affect the stroke colour (via an interpolation between the original colour & grey). The tip fill colour is a brighter version of the stroke colour. Very happy for adjustments here, although I think it's important that
tipOpacityFn(close-to-zero) -> 0
so that we don't change how the majority of datasets appear.