molstar
Version:
A comprehensive macromolecular library.
122 lines (121 loc) • 5.38 kB
JavaScript
/**
* Copyright (c) 2026 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3 } from '../../../mol-math/linear-algebra.js';
import { Structure, StructureElement, Unit } from '../../../mol-model/structure.js';
import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder.js';
import { ComplexMeshParams, ComplexMeshVisual } from '../complex-visual.js';
import { ParamDefinition as PD } from '../../../mol-util/param-definition.js';
import { LocationIterator } from '../../../mol-geo/util/location-iterator.js';
import { PickingId } from '../../../mol-geo/geometry/picking.js';
import { Interval, OrderedSet } from '../../../mol-data/int.js';
import { EmptyLoci } from '../../../mol-model/loci.js';
import { convexHull } from '../../../mol-math/geometry/convex-hull.js';
import { SortedArray } from '../../../mol-data/int/sorted-array.js';
export const CoordinationPolyhedronMeshParams = {
...ComplexMeshParams,
includeParent: PD.Boolean(false),
minCoordination: PD.Numeric(4, { min: 4, max: 12, step: 1 }, { description: 'Minimum number of coordinating atoms to draw a polyhedron' }),
maxCoordination: PD.Numeric(12, { min: 4, max: 24, step: 1 }, { description: 'Maximum coordination number' }),
};
function createCoordinationPolyhedronMesh(ctx, structure, theme, props, mesh) {
const { minCoordination, maxCoordination } = props;
const { child, coordination: { sites, eachLigand } } = structure;
const count = sites.count * 4;
const builderState = MeshBuilder.createState(count, count / 2, mesh);
for (let i = 0; i < sites.count; i++) {
const number = sites.numbers[i];
if (number < minCoordination || number > maxCoordination)
continue;
if (child) {
const unit = structure.unitMap.get(sites.unitIds[i]);
const childUnit = child.unitMap.get(unit.id);
const element = unit.elements[sites.indices[i]];
if (!childUnit || !SortedArray.has(childUnit.elements, element))
continue;
}
const positions = [];
eachLigand(i, l => {
const p = l.unit.conformation.position(l.element, Vec3());
positions.push(p);
});
const hull = convexHull(positions);
if (hull) {
builderState.currentGroup = i;
for (let i = 0; i < hull.indices.length; i += 3) {
const a = positions[hull.indices[i]];
const b = positions[hull.indices[i + 1]];
const c = positions[hull.indices[i + 2]];
MeshBuilder.addTriangle(builderState, a, b, c);
}
}
}
return MeshBuilder.getMesh(builderState);
}
function CoordinationPolyhedronIterator(structure, props) {
const { sites } = structure.coordination;
const groupCount = sites.count;
const instanceCount = 1;
const location = StructureElement.Location.create(structure);
function getLocation(groupIndex) {
if (groupIndex < sites.count) {
const u = structure.unitMap.get(sites.unitIds[groupIndex]);
location.unit = u;
location.element = u.elements[sites.indices[groupIndex]];
}
return location;
}
return LocationIterator(groupCount, instanceCount, 1, getLocation, true);
}
function getCoordinationPolyhedronLoci(pickingId, structure, id) {
const { objectId, groupId } = pickingId;
if (id === objectId) {
if (groupId === PickingId.Null) {
return Structure.Loci(structure);
}
const { sites } = structure.coordination;
if (groupId < sites.count) {
return StructureElement.Loci(structure, [{
unit: structure.unitMap.get(sites.unitIds[groupId]),
indices: OrderedSet.ofSingleton(sites.indices[groupId])
}]);
}
return Structure.Loci(structure);
}
return EmptyLoci;
}
function eachCoordinationPolyhedron(loci, structure, apply) {
let changed = false;
if (!StructureElement.Loci.is(loci))
return false;
if (!Structure.areEquivalent(loci.structure, structure))
return false;
const { getSiteIndex } = structure.coordination;
for (const { unit, indices } of loci.elements) {
if (!Unit.isAtomic(unit))
continue;
OrderedSet.forEach(indices, v => {
const groupIndex = getSiteIndex(unit, unit.elements[v]);
if (groupIndex >= 0) {
if (apply(Interval.ofSingleton(groupIndex)))
changed = true;
}
});
}
return changed;
}
export function CoordinationPolyhedronMeshVisual(materialId) {
return ComplexMeshVisual({
defaultProps: PD.getDefaultValues(CoordinationPolyhedronMeshParams),
createGeometry: createCoordinationPolyhedronMesh,
createLocationIterator: CoordinationPolyhedronIterator,
getLoci: getCoordinationPolyhedronLoci,
eachLocation: eachCoordinationPolyhedron,
setUpdateState: (state, newProps, currentProps) => {
state.createGeometry = (newProps.minCoordination !== currentProps.minCoordination ||
newProps.maxCoordination !== currentProps.maxCoordination);
}
}, materialId);
}