molstar
Version:
A comprehensive macromolecular library.
112 lines (111 loc) • 4.69 kB
JavaScript
/**
* Copyright (c) 2021-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ColorTheme } from '../color';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { ColorNames } from '../../mol-util/color/names';
import { Volume } from '../../mol-model/volume/volume';
import { ColorThemeCategory } from './categories';
import { clamp, normalize } from '../../mol-math/interpolate';
import { Color } from '../../mol-util/color/color';
import { Grid } from '../../mol-model/volume/grid';
import { isPositionLocation } from '../../mol-geo/util/location-iterator';
const Description = 'Assign color based on the given value of a volume cell.';
export const VolumeValueColorThemeParams = {
colorList: PD.ColorList({
kind: 'interpolate',
colors: [
[ColorNames.white, 0],
[ColorNames.red, 0.25],
[ColorNames.white, 0.5],
[ColorNames.blue, 0.75],
[ColorNames.white, 1]
]
}, { offsets: true, isEssential: true }),
domain: PD.MappedStatic('auto', {
custom: PD.Interval([-1, 1], { step: 0.001 }),
auto: PD.Group({
symmetric: PD.Boolean(false, { description: 'If true the automatic range is determined as [-|max|, |max|].' })
})
}),
isRelative: PD.Boolean(false, { description: 'If true the value is treated as relative to the volume mean and sigma.' }),
defaultColor: PD.Color(Color(0xcccccc)),
};
export function getVolumeValueColorThemeParams(ctx) {
return VolumeValueColorThemeParams; // TODO return copy
}
export function VolumeValueColorTheme(ctx, props) {
var _a;
if (ctx.volume) {
const { min, max, mean, sigma } = ctx.volume.grid.stats;
const domain = props.domain.name === 'custom' ? props.domain.params : [min, max];
const { colorList, defaultColor } = props;
if (props.domain.name === 'auto' && props.isRelative) {
domain[0] = (domain[0] - mean) / sigma;
domain[1] = (domain[1] - mean) / sigma;
}
if (props.domain.name === 'auto' && props.domain.params.symmetric) {
const max = Math.max(Math.abs(domain[0]), Math.abs(domain[1]));
domain[0] = -max;
domain[1] = max;
}
if ((_a = ctx.locationKinds) === null || _a === void 0 ? void 0 : _a.includes('direct-location')) {
// this is for direct-volume rendering where the location is the volume grid cell
// and we only need to provide the domain and palette here
const normalizedDomain = [
normalize(domain[0], min, max),
normalize(domain[1], min, max)
];
const palette = ColorTheme.Palette(colorList.colors, colorList.kind, normalizedDomain, defaultColor);
return {
factory: VolumeValueColorTheme,
granularity: 'direct',
props,
description: Description,
palette,
};
}
else {
const getTrilinearlyInterpolated = Grid.makeGetTrilinearlyInterpolated(ctx.volume.grid, 'none');
const color = (location) => {
if (!isPositionLocation(location)) {
return props.defaultColor;
}
const value = getTrilinearlyInterpolated(location.position);
if (isNaN(value))
return props.defaultColor;
return (clamp((value - domain[0]) / (domain[1] - domain[0]), 0, 1) * ColorTheme.PaletteScale);
};
const palette = ColorTheme.Palette(colorList.colors, colorList.kind, undefined, defaultColor);
return {
factory: VolumeValueColorTheme,
granularity: 'vertex',
preferSmoothing: true,
color,
palette,
props,
description: Description,
};
}
}
else {
return {
factory: VolumeValueColorTheme,
granularity: 'uniform',
color: () => props.defaultColor,
props,
description: Description,
};
}
}
export const VolumeValueColorThemeProvider = {
name: 'volume-value',
label: 'Volume Value',
category: ColorThemeCategory.Misc,
factory: VolumeValueColorTheme,
getParams: getVolumeValueColorThemeParams,
defaultValues: PD.getDefaultValues(VolumeValueColorThemeParams),
isApplicable: (ctx) => !!ctx.volume && !Volume.Segmentation.get(ctx.volume),
};