UNPKG

molstar

Version:

A comprehensive macromolecular library.

122 lines (121 loc) 5.38 kB
/** * 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); }