UNPKG

molstar

Version:

A comprehensive macromolecular library.

247 lines (246 loc) 11.3 kB
/** * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author David Sehnal <david.sehnal@gmail.com> */ import { Vec3 } from '../../../../mol-math/linear-algebra'; import { Unit, StructureElement, Structure } from '../../../../mol-model/structure'; import { EmptyLoci } from '../../../../mol-model/loci'; import { Interval, OrderedSet, SortedArray } from '../../../../mol-data/int'; import { Mesh } from '../../../../mol-geo/geometry/mesh/mesh'; import { sphereVertexCount } from '../../../../mol-geo/primitive/sphere'; import { MeshBuilder } from '../../../../mol-geo/geometry/mesh/mesh-builder'; import { addSphere } from '../../../../mol-geo/geometry/mesh/builder/sphere'; import { LocationIterator } from '../../../../mol-geo/util/location-iterator'; import { Spheres } from '../../../../mol-geo/geometry/spheres/spheres'; import { SpheresBuilder } from '../../../../mol-geo/geometry/spheres/spheres-builder'; import { isTrace, isH } from './common'; import { Sphere3D } from '../../../../mol-math/geometry'; // avoiding namespace lookup improved performance in Chrome (Aug 2020) var v3add = Vec3.add; export function makeElementIgnoreTest(structure, unit, props) { var ignoreHydrogens = props.ignoreHydrogens, traceOnly = props.traceOnly; var atomicNumber = unit.model.atomicHierarchy.derived.atom.atomicNumber; var isCoarse = Unit.isCoarse(unit); var child = structure.child; var childUnit = child === null || child === void 0 ? void 0 : child.unitMap.get(unit.id); if (child && !childUnit) throw new Error('expected childUnit to exist if child exists'); if (!child && !ignoreHydrogens && !traceOnly) return; return function (element) { return ((!!childUnit && !SortedArray.has(childUnit.elements, element)) || (!isCoarse && ignoreHydrogens && isH(atomicNumber, element)) || (traceOnly && !isTrace(unit, element))); }; } export function createElementSphereMesh(ctx, unit, structure, theme, props, mesh) { var child = structure.child; var childUnit = child === null || child === void 0 ? void 0 : child.unitMap.get(unit.id); if (child && !childUnit) return Mesh.createEmpty(mesh); var detail = props.detail, sizeFactor = props.sizeFactor; var elements = unit.elements; var elementCount = elements.length; var vertexCount = elementCount * sphereVertexCount(detail); var builderState = MeshBuilder.createState(vertexCount, vertexCount / 2, mesh); var v = Vec3(); var pos = unit.conformation.invariantPosition; var ignore = makeElementIgnoreTest(structure, unit, props); var l = StructureElement.Location.create(structure, unit); var themeSize = theme.size.size; var center = Vec3(); var maxSize = 0; var count = 0; for (var i = 0; i < elementCount; i++) { if (ignore && ignore(elements[i])) continue; l.element = elements[i]; pos(elements[i], v); v3add(center, center, v); count += 1; builderState.currentGroup = i; var size = themeSize(l); if (size > maxSize) maxSize = size; addSphere(builderState, v, size * sizeFactor, detail); } var oldBoundingSphere = mesh ? Sphere3D.clone(mesh.boundingSphere) : undefined; var m = MeshBuilder.getMesh(builderState); if (count === 0) return m; // re-use boundingSphere if it has not changed much var boundingSphere; Vec3.scale(center, center, 1 / count); if (oldBoundingSphere && Vec3.distance(center, oldBoundingSphere.center) / oldBoundingSphere.radius < 1.0) { boundingSphere = oldBoundingSphere; } else { boundingSphere = Sphere3D.expand(Sphere3D(), (childUnit !== null && childUnit !== void 0 ? childUnit : unit).boundary.sphere, maxSize * sizeFactor + 0.05); } m.setBoundingSphere(boundingSphere); return m; } export function createElementSphereImpostor(ctx, unit, structure, theme, props, spheres) { var child = structure.child; var childUnit = child === null || child === void 0 ? void 0 : child.unitMap.get(unit.id); if (child && !childUnit) return Spheres.createEmpty(spheres); var elements = unit.elements; var elementCount = elements.length; var builder = SpheresBuilder.create(elementCount, elementCount / 2, spheres); var v = Vec3(); var pos = unit.conformation.invariantPosition; var ignore = makeElementIgnoreTest(structure, unit, props); var l = StructureElement.Location.create(structure, unit); var themeSize = theme.size.size; var center = Vec3(); var maxSize = 0; var count = 0; for (var i = 0; i < elementCount; i++) { if (ignore === null || ignore === void 0 ? void 0 : ignore(elements[i])) continue; pos(elements[i], v); builder.add(v[0], v[1], v[2], i); v3add(center, center, v); count += 1; l.element = elements[i]; var size = themeSize(l); if (size > maxSize) maxSize = size; } var oldBoundingSphere = spheres ? Sphere3D.clone(spheres.boundingSphere) : undefined; var s = builder.getSpheres(); if (count === 0) return s; // re-use boundingSphere if it has not changed much var boundingSphere; Vec3.scale(center, center, 1 / count); if (oldBoundingSphere && Vec3.distance(center, oldBoundingSphere.center) / oldBoundingSphere.radius < 1.0) { boundingSphere = oldBoundingSphere; } else { boundingSphere = Sphere3D.expand(Sphere3D(), (childUnit !== null && childUnit !== void 0 ? childUnit : unit).boundary.sphere, maxSize * props.sizeFactor + 0.05); } s.setBoundingSphere(boundingSphere); return s; } export function eachElement(loci, structureGroup, apply) { var changed = false; if (!StructureElement.Loci.is(loci)) return false; var structure = structureGroup.structure, group = structureGroup.group; if (!Structure.areEquivalent(loci.structure, structure)) return false; var elementCount = group.elements.length; var unitIndexMap = group.unitIndexMap; for (var _a = 0, _b = loci.elements; _a < _b.length; _a++) { var e = _b[_a]; var unitIdx = unitIndexMap.get(e.unit.id); if (unitIdx !== undefined) { var offset = unitIdx * elementCount; // to target unit instance if (Interval.is(e.indices)) { var start = offset + Interval.start(e.indices); var end = offset + Interval.end(e.indices); if (apply(Interval.ofBounds(start, end))) changed = true; } else { for (var i = 0, _i = e.indices.length; i < _i; i++) { var start = e.indices[i]; var endI = i + 1; while (endI < _i && e.indices[endI] === start) endI++; i = endI - 1; var end = e.indices[i]; changed = apply(Interval.ofRange(offset + start, offset + end)) || changed; } } } } return changed; } export function getElementLoci(pickingId, structureGroup, id) { var objectId = pickingId.objectId, instanceId = pickingId.instanceId, groupId = pickingId.groupId; if (id === objectId) { var structure = structureGroup.structure, group = structureGroup.group; var unit = group.units[instanceId]; var indices = OrderedSet.ofSingleton(groupId); return StructureElement.Loci(structure.target, [{ unit: unit, indices: indices }]); } return EmptyLoci; } // export function eachSerialElement(loci, structure, apply) { var changed = false; if (!StructureElement.Loci.is(loci)) return false; if (!Structure.areEquivalent(loci.structure, structure)) return false; var cumulativeUnitElementCount = structure.serialMapping.cumulativeUnitElementCount; for (var _a = 0, _b = loci.elements; _a < _b.length; _a++) { var e = _b[_a]; var unitIdx = structure.unitIndexMap.get(e.unit.id); if (unitIdx !== undefined) { if (Interval.is(e.indices)) { var start = cumulativeUnitElementCount[unitIdx] + Interval.start(e.indices); var end = cumulativeUnitElementCount[unitIdx] + Interval.end(e.indices); if (apply(Interval.ofBounds(start, end))) changed = true; } else { for (var i = 0, _i = e.indices.length; i < _i; i++) { var idx = cumulativeUnitElementCount[unitIdx] + e.indices[i]; if (apply(Interval.ofSingleton(idx))) changed = true; } } } } return changed; } export function getSerialElementLoci(pickingId, structure, id) { var objectId = pickingId.objectId, groupId = pickingId.groupId; if (id === objectId) { var _a = structure.serialMapping, unitIndices = _a.unitIndices, cumulativeUnitElementCount = _a.cumulativeUnitElementCount; var unitIdx = unitIndices[groupId]; var unit = structure.units[unitIdx]; var idx = groupId - cumulativeUnitElementCount[unitIdx]; var indices = OrderedSet.ofSingleton(idx); return StructureElement.Loci(structure, [{ unit: unit, indices: indices }]); } return EmptyLoci; } // export var ElementIterator; (function (ElementIterator) { function fromGroup(structureGroup) { var group = structureGroup.group, structure = structureGroup.structure; var groupCount = group.elements.length; var instanceCount = group.units.length; var location = StructureElement.Location.create(structure); var getLocation = function (groupIndex, instanceIndex) { var unit = group.units[instanceIndex]; location.unit = unit; location.element = unit.elements[groupIndex]; return location; }; return LocationIterator(groupCount, instanceCount, 1, getLocation); } ElementIterator.fromGroup = fromGroup; function fromStructure(structure) { var units = structure.units, elementCount = structure.elementCount; var groupCount = elementCount; var instanceCount = 1; var _a = structure.serialMapping, unitIndices = _a.unitIndices, elementIndices = _a.elementIndices; var location = StructureElement.Location.create(structure); var getLocation = function (groupIndex) { location.unit = units[unitIndices[groupIndex]]; location.element = elementIndices[groupIndex]; return location; }; return LocationIterator(groupCount, instanceCount, 1, getLocation, true); } ElementIterator.fromStructure = fromStructure; })(ElementIterator || (ElementIterator = {}));