molstar
Version:
A comprehensive macromolecular library.
166 lines (165 loc) • 8.74 kB
JavaScript
/**
* Copyright (c) 2018-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DirectVolumeRepresentationProvider = exports.DirectVolumeParams = void 0;
exports.createDirectVolume2d = createDirectVolume2d;
exports.createDirectVolume3d = createDirectVolume3d;
exports.createDirectVolume = createDirectVolume;
exports.getDirectVolumeLoci = getDirectVolumeLoci;
exports.eachDirectVolume = eachDirectVolume;
exports.getDirectVolumeParams = getDirectVolumeParams;
exports.DirectVolumeVisual = DirectVolumeVisual;
exports.DirectVolumeRepresentation = DirectVolumeRepresentation;
const param_definition_1 = require("../../mol-util/param-definition");
const linear_algebra_1 = require("../../mol-math/linear-algebra");
const geometry_1 = require("../../mol-math/geometry");
const volume_1 = require("../../mol-model/volume");
const direct_volume_1 = require("../../mol-geo/geometry/direct-volume/direct-volume");
const representation_1 = require("./representation");
const location_iterator_1 = require("../../mol-geo/util/location-iterator");
const location_1 = require("../../mol-model/location");
const int_1 = require("../../mol-data/int");
const loci_1 = require("../../mol-model/loci");
const util_1 = require("./util");
function getBoundingBox(gridDimension, transform) {
const bbox = (0, geometry_1.Box3D)();
geometry_1.Box3D.add(bbox, gridDimension);
geometry_1.Box3D.transform(bbox, bbox, transform);
return bbox;
}
// 2d volume texture
function createDirectVolume2d(ctx, webgl, volume, props, directVolume) {
const gridDimension = volume.grid.cells.space.dimensions;
const { width, height } = (0, util_1.getVolumeTexture2dLayout)(gridDimension);
if (Math.max(width, height) > webgl.maxTextureSize / 2) {
throw new Error('volume too large for direct-volume rendering');
}
const dataType = props.dataType === 'halfFloat' && !webgl.extensions.textureHalfFloat ? 'float' : props.dataType;
const textureImage = (0, util_1.createVolumeTexture2d)(volume, 'normals', 0, dataType);
// debugTexture(createImageData(textureImage.array, textureImage.width, textureImage.height), 1/3)
const transform = volume_1.Grid.getGridToCartesianTransform(volume.grid);
const bbox = getBoundingBox(gridDimension, transform);
let texture;
if (directVolume && directVolume.dataType.ref.value === dataType) {
texture = directVolume.gridTexture.ref.value;
}
else {
texture = dataType === 'byte'
? webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear')
: dataType === 'halfFloat'
? webgl.resources.texture('image-float16', 'rgba', 'fp16', 'linear')
: webgl.resources.texture('image-float32', 'rgba', 'float', 'linear');
}
texture.load(textureImage);
const { unitToCartn, cellDim } = getUnitToCartn(volume.grid);
const axisOrder = volume.grid.cells.space.axisOrderSlowToFast;
return direct_volume_1.DirectVolume.create(bbox, gridDimension, transform, unitToCartn, cellDim, texture, volume.grid.stats, false, axisOrder, dataType, directVolume);
}
// 3d volume texture
function getUnitToCartn(grid) {
if (grid.transform.kind === 'matrix') {
return {
unitToCartn: linear_algebra_1.Mat4.mul((0, linear_algebra_1.Mat4)(), grid.transform.matrix, linear_algebra_1.Mat4.fromScaling((0, linear_algebra_1.Mat4)(), grid.cells.space.dimensions)),
cellDim: linear_algebra_1.Mat4.getScaling((0, linear_algebra_1.Vec3)(), grid.transform.matrix)
};
}
const box = grid.transform.fractionalBox;
const size = geometry_1.Box3D.size((0, linear_algebra_1.Vec3)(), box);
return {
unitToCartn: linear_algebra_1.Mat4.mul3((0, linear_algebra_1.Mat4)(), grid.transform.cell.fromFractional, linear_algebra_1.Mat4.fromTranslation((0, linear_algebra_1.Mat4)(), box.min), linear_algebra_1.Mat4.fromScaling((0, linear_algebra_1.Mat4)(), size)),
cellDim: linear_algebra_1.Vec3.div((0, linear_algebra_1.Vec3)(), grid.transform.cell.size, grid.cells.space.dimensions)
};
}
function createDirectVolume3d(ctx, webgl, volume, props, directVolume) {
const gridDimension = volume.grid.cells.space.dimensions;
if (Math.max(...gridDimension) > webgl.max3dTextureSize / 2) {
throw new Error('volume too large for direct-volume rendering');
}
const dataType = props.dataType === 'halfFloat' && !webgl.extensions.textureHalfFloat ? 'float' : props.dataType;
const textureVolume = (0, util_1.createVolumeTexture3d)(volume, dataType);
const transform = volume_1.Grid.getGridToCartesianTransform(volume.grid);
const bbox = getBoundingBox(gridDimension, transform);
let texture;
if (directVolume && directVolume.dataType.ref.value === dataType) {
texture = directVolume.gridTexture.ref.value;
}
else {
texture = dataType === 'byte'
? webgl.resources.texture('volume-uint8', 'rgba', 'ubyte', 'linear')
: dataType === 'halfFloat'
? webgl.resources.texture('volume-float16', 'rgba', 'fp16', 'linear')
: webgl.resources.texture('volume-float32', 'rgba', 'float', 'linear');
}
texture.load(textureVolume);
const { unitToCartn, cellDim } = getUnitToCartn(volume.grid);
const axisOrder = volume.grid.cells.space.axisOrderSlowToFast;
return direct_volume_1.DirectVolume.create(bbox, gridDimension, transform, unitToCartn, cellDim, texture, volume.grid.stats, false, axisOrder, dataType, directVolume);
}
//
async function createDirectVolume(ctx, volume, key, theme, props, directVolume) {
const { runtime, webgl } = ctx;
if (webgl === undefined)
throw new Error('DirectVolumeVisual requires `webgl` in VisualContext');
return webgl.isWebGL2 ?
createDirectVolume3d(runtime, webgl, volume, props, directVolume) :
createDirectVolume2d(runtime, webgl, volume, props, directVolume);
}
function getLoci(volume, props) {
return volume_1.Volume.Loci(volume);
}
function getDirectVolumeLoci(pickingId, volume, key, props, id) {
const { objectId, groupId } = pickingId;
if (id === objectId) {
return volume_1.Volume.Cell.Loci(volume, int_1.Interval.ofSingleton(groupId));
}
return loci_1.EmptyLoci;
}
function eachDirectVolume(loci, volume, key, props, apply) {
return (0, util_1.eachVolumeLoci)(loci, volume, undefined, apply);
}
//
exports.DirectVolumeParams = {
...direct_volume_1.DirectVolume.Params,
quality: { ...direct_volume_1.DirectVolume.Params.quality, isEssential: false },
dataType: param_definition_1.ParamDefinition.Select('byte', param_definition_1.ParamDefinition.arrayToOptions(['byte', 'float', 'halfFloat'])),
};
function getDirectVolumeParams(ctx, volume) {
const params = param_definition_1.ParamDefinition.clone(exports.DirectVolumeParams);
params.controlPoints.getVolume = () => volume;
return params;
}
function DirectVolumeVisual(materialId) {
return (0, representation_1.VolumeVisual)({
defaultProps: param_definition_1.ParamDefinition.getDefaultValues(exports.DirectVolumeParams),
createGeometry: createDirectVolume,
createLocationIterator: (volume) => (0, location_iterator_1.LocationIterator)(volume.grid.cells.data.length, 1, 1, () => location_1.NullLocation),
getLoci: getDirectVolumeLoci,
eachLocation: eachDirectVolume,
setUpdateState: (state, volume, newProps, currentProps) => {
state.createGeometry = newProps.dataType !== currentProps.dataType;
},
geometryUtils: direct_volume_1.DirectVolume.Utils,
dispose: (geometry) => {
geometry.gridTexture.ref.value.destroy();
},
}, materialId);
}
function DirectVolumeRepresentation(ctx, getParams) {
return (0, representation_1.VolumeRepresentation)('Direct Volume', ctx, getParams, DirectVolumeVisual, getLoci);
}
exports.DirectVolumeRepresentationProvider = (0, representation_1.VolumeRepresentationProvider)({
name: 'direct-volume',
label: 'Direct Volume',
description: 'Direct rendering of volumetric data.',
factory: DirectVolumeRepresentation,
getParams: getDirectVolumeParams,
defaultValues: param_definition_1.ParamDefinition.getDefaultValues(exports.DirectVolumeParams),
defaultColorTheme: { name: 'volume-value' },
defaultSizeTheme: { name: 'uniform' },
locationKinds: ['position-location', 'direct-location'],
isApplicable: (volume) => !volume_1.Volume.isEmpty(volume) && !volume_1.Volume.Segmentation.get(volume)
});
;