molstar
Version:
A comprehensive macromolecular library.
247 lines (246 loc) • 11.3 kB
JavaScript
/**
* 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 = {}));