UNPKG

molstar

Version:

A comprehensive macromolecular library.

253 lines (252 loc) 11.9 kB
"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); }