molstar
Version:
A comprehensive macromolecular library.
253 lines (252 loc) • 11.9 kB
JavaScript
"use strict";
/**
* Copyright (c) 2025-2026 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Ludovic Autin <autin@scripps.edu>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.CommonStreamlinesParams = void 0;
exports.StreamlinesLocation = StreamlinesLocation;
exports.isStreamlinesLocation = isStreamlinesLocation;
exports.areStreamlinesLocationsEqual = areStreamlinesLocationsEqual;
exports.streamlinesLocationLabel = streamlinesLocationLabel;
exports.streamlinesLociLabel = streamlinesLociLabel;
exports.StreamlinesLoci = StreamlinesLoci;
exports.isStreamlinesLoci = isStreamlinesLoci;
exports.getStreamlinesLociSize = getStreamlinesLociSize;
exports.getStreamlinesLociBoundingSphere = getStreamlinesLociBoundingSphere;
exports.areStreamlinesLociEqual = areStreamlinesLociEqual;
exports.isStreamlinesLociEmpty = isStreamlinesLociEmpty;
exports.streamlinePassesFilter = streamlinePassesFilter;
exports.getStreamlinesVisualLoci = getStreamlinesVisualLoci;
exports.getStreamlinesLoci = getStreamlinesLoci;
exports.eachStreamlines = eachStreamlines;
exports.createStreamlinesLocationIterator = createStreamlinesLocationIterator;
const interval_1 = require("../../../mol-data/int/interval.js");
const ordered_set_1 = require("../../../mol-data/int/ordered-set.js");
const location_iterator_1 = require("../../../mol-geo/util/location-iterator.js");
const location_1 = require("../../../mol-model/location.js");
const loci_1 = require("../../../mol-model/loci.js");
const grid_1 = require("../../../mol-model/volume/grid.js");
const volume_1 = require("../../../mol-model/volume/volume.js");
const streamlines_1 = require("../streamlines.js");
const param_definition_1 = require("../../../mol-util/param-definition.js");
const boundary_helper_1 = require("../../../mol-math/geometry/boundary-helper.js");
const vec3_1 = require("../../../mol-math/linear-algebra/3d/vec3.js");
function StreamlinesLocation(streamlines, volume, index, instance) {
return (0, location_1.DataLocation)('streamlines', { volume, streamlines }, { index: index, instance: instance });
}
function isStreamlinesLocation(x) {
return !!x && x.kind === 'data-location' && x.tag === 'streamlines';
}
function areStreamlinesLocationsEqual(locA, locB) {
return (locA.data.volume === locB.data.volume &&
locA.data.streamlines === locB.data.streamlines &&
locA.element.index === locB.element.index &&
locA.element.instance === locB.element.instance);
}
function streamlinesLocationLabel(streamlines, volume, index, instance) {
const label = [
`${volume.label || 'Volume'}`,
`Streamline #${index}`
];
if (volume.instances.length > 1) {
label.push(`Instance #${instance}`);
}
return label.join(' | ');
}
function streamlinesLociLabel(streamlines, volume, elements) {
const size = getStreamlinesLociSize(elements);
const label = [
`${volume.label || 'Volume'}`
];
if (size === 0) {
label.push('No Streamlines');
}
else if (size === 1) {
const index = ordered_set_1.OrderedSet.start(elements[0].indices);
label.push(`Streamline #${index}`);
if (volume.instances.length > 1) {
const instance = ordered_set_1.OrderedSet.start(elements[0].instances);
label.push(`Instance #${instance}`);
}
}
else {
label.push(`${size} Streamlines`);
}
return label.join(' | ');
}
function StreamlinesLoci(streamlines, volume, elements) {
return (0, loci_1.DataLoci)('streamlines', { streamlines, volume }, elements, (boundingSphere) => getStreamlinesLociBoundingSphere(streamlines, volume, elements, boundingSphere), () => streamlinesLociLabel(streamlines, volume, elements));
}
function isStreamlinesLoci(x) {
return !!x && x.kind === 'data-loci' && x.tag === 'streamlines';
}
function getStreamlinesLociSize(elements) {
let size = 0;
for (const e of elements) {
size += ordered_set_1.OrderedSet.size(e.indices) * ordered_set_1.OrderedSet.size(e.instances);
}
return size;
}
const boundaryHelper = new boundary_helper_1.BoundaryHelper('98');
const tmpBoundaryPos = (0, vec3_1.Vec3)();
const tmpBoundaryPos2 = (0, vec3_1.Vec3)();
function getStreamlinesLociBoundingSphere(streamlines, volume, elements, boundingSphere) {
boundaryHelper.reset();
const transform = grid_1.Grid.getGridToCartesianTransform(volume.grid);
for (const { indices, instances } of elements) {
for (let i = 0, _i = ordered_set_1.OrderedSet.size(indices); i < _i; i++) {
const o = ordered_set_1.OrderedSet.getAt(indices, i);
for (const p of streamlines[o]) {
vec3_1.Vec3.transformMat4(tmpBoundaryPos, p, transform);
for (let j = 0, _j = ordered_set_1.OrderedSet.size(instances); j < _j; j++) {
const instance = volume.instances[ordered_set_1.OrderedSet.getAt(instances, j)];
vec3_1.Vec3.transformMat4(tmpBoundaryPos2, tmpBoundaryPos, instance.transform);
boundaryHelper.includePosition(tmpBoundaryPos2);
}
}
}
}
boundaryHelper.finishedIncludeStep();
for (const { indices, instances } of elements) {
for (let i = 0, _i = ordered_set_1.OrderedSet.size(indices); i < _i; i++) {
const o = ordered_set_1.OrderedSet.getAt(indices, i);
for (const p of streamlines[o]) {
vec3_1.Vec3.transformMat4(tmpBoundaryPos, p, transform);
for (let j = 0, _j = ordered_set_1.OrderedSet.size(instances); j < _j; j++) {
const instance = volume.instances[ordered_set_1.OrderedSet.getAt(instances, j)];
vec3_1.Vec3.transformMat4(tmpBoundaryPos2, tmpBoundaryPos, instance.transform);
boundaryHelper.radiusPosition(tmpBoundaryPos2);
}
}
}
}
return boundaryHelper.getSphere(boundingSphere);
}
function areStreamlinesLociEqual(a, b) {
if (a.data.volume !== b.data.volume || a.elements.length !== b.elements.length)
return false;
for (let i = 0, il = a.elements.length; i < il; ++i) {
const ae = a.elements[i], be = b.elements[i];
if (!ordered_set_1.OrderedSet.areEqual(ae.instances, be.instances) ||
!ordered_set_1.OrderedSet.areEqual(ae.indices, be.indices))
return false;
}
return true;
}
function isStreamlinesLociEmpty(loci) {
for (const { indices, instances } of loci.elements) {
if (!ordered_set_1.OrderedSet.isEmpty(indices) || !ordered_set_1.OrderedSet.isEmpty(instances))
return false;
}
return true;
}
//
exports.CommonStreamlinesParams = {
anchorEnabled: param_definition_1.ParamDefinition.Boolean(false, { label: 'Anchor' }),
anchorCenter: param_definition_1.ParamDefinition.Vec3((0, vec3_1.Vec3)(), undefined, { hideIf: p => !p.anchorEnabled }),
anchorRadius: param_definition_1.ParamDefinition.Numeric(1, { min: 0, max: 10, step: 0.1 }, { hideIf: p => !p.anchorEnabled }),
dashEnabled: param_definition_1.ParamDefinition.Boolean(false, { label: 'Dash' }),
dashPoints: param_definition_1.ParamDefinition.Numeric(5, { min: 1, max: 25, step: 1 }, { description: 'Number of streamline points per dash/gap', hideIf: p => !p.dashEnabled }),
dashShift: param_definition_1.ParamDefinition.Boolean(false, { description: 'Shift dashes so dashes become gaps and vice versa', hideIf: p => !p.dashEnabled }),
};
const tmpFilterVec = (0, vec3_1.Vec3)();
/**
* Check if a streamline passes the sphere filter.
* A streamline passes if its start or end point is within the filter sphere.
*/
function streamlinePassesFilter(streamline, gridToCartn, props) {
if (streamline.length < 2)
return false;
if (!props.anchorEnabled)
return true;
// Check start point
const start = streamline[0];
vec3_1.Vec3.transformMat4(tmpFilterVec, start, gridToCartn);
if (vec3_1.Vec3.distance(tmpFilterVec, props.anchorCenter) <= props.anchorRadius)
return true;
// Check end point
const end = streamline[streamline.length - 1];
vec3_1.Vec3.transformMat4(tmpFilterVec, end, gridToCartn);
if (vec3_1.Vec3.distance(tmpFilterVec, props.anchorCenter) <= props.anchorRadius)
return true;
return false;
}
function getStreamlinesVisualLoci(volume, _props) {
const streamlines = streamlines_1.StreamlinesProvider.get(volume).value;
const indices = interval_1.Interval.ofLength(streamlines.length);
const instances = interval_1.Interval.ofLength(volume.instances.length);
return StreamlinesLoci(streamlines, volume, [{ indices, instances }]);
}
function getStreamlinesLoci(pickingId, volume, _key, _props, id) {
const { objectId, groupId, instanceId } = pickingId;
if (id === objectId) {
const granularity = volume_1.Volume.PickingGranularity.get(volume);
const instances = ordered_set_1.OrderedSet.ofSingleton(instanceId);
if (granularity === 'volume')
return volume_1.Volume.Loci(volume, instances);
const streamlines = streamlines_1.StreamlinesProvider.get(volume).value;
const indices = ordered_set_1.OrderedSet.ofSingleton(groupId);
return StreamlinesLoci(streamlines, volume, [{ indices, instances }]);
}
return loci_1.EmptyLoci;
}
function eachStreamlines(loci, volume, _key, _props, apply) {
let changed = false;
const streamlines = streamlines_1.StreamlinesProvider.get(volume).value;
const count = streamlines.length;
if (volume_1.Volume.isLoci(loci)) {
if (!volume_1.Volume.areEquivalent(loci.volume, volume))
return false;
if (interval_1.Interval.is(loci.instances)) {
const start = interval_1.Interval.start(loci.instances) * count;
const end = interval_1.Interval.end(loci.instances) * count;
if (apply(interval_1.Interval.ofBounds(start, end)))
changed = true;
}
else {
for (let i = 0, il = loci.instances.length; i < il; ++i) {
const offset = loci.instances[i] * count;
if (apply(interval_1.Interval.ofBounds(offset, offset + count)))
changed = true;
}
}
}
else if (isStreamlinesLoci(loci)) {
if (!volume_1.Volume.areEquivalent(loci.data.volume, volume))
return false;
for (const { indices, instances } of loci.elements) {
if (interval_1.Interval.is(indices)) {
ordered_set_1.OrderedSet.forEach(instances, j => {
const offset = j * count;
if (apply(interval_1.Interval.offset(indices, offset)))
changed = true;
});
}
else {
ordered_set_1.OrderedSet.forEach(indices, v => {
ordered_set_1.OrderedSet.forEach(instances, j => {
const offset = j * count;
if (apply(interval_1.Interval.ofSingleton(offset + v)))
changed = true;
});
});
}
}
}
return changed;
}
function createStreamlinesLocationIterator(volume) {
const streamlines = streamlines_1.StreamlinesProvider.get(volume).value;
const groupCount = streamlines.length;
const instanceCount = volume.instances.length;
const l = StreamlinesLocation(streamlines, volume);
const getLocation = (groupIndex, instanceIndex) => {
l.element.index = groupIndex;
l.element.instance = instanceIndex;
return l;
};
return (0, location_iterator_1.LocationIterator)(groupCount, instanceCount, 1, getLocation);
}