diff --git a/src/mol-model/structure/structure/symmetry.ts b/src/mol-model/structure/structure/symmetry.ts index 6dbd5de05659c58bfaf2d7a46346d2baf49f8249..6568f9cb4f41af97513ea1d51376dd958f85f628 100644 --- a/src/mol-model/structure/structure/symmetry.ts +++ b/src/mol-model/structure/structure/symmetry.ts @@ -191,7 +191,13 @@ async function findMatesRadius(ctx: RuntimeContext, structure: Structure, radius const operators = getOperatorsCached333(symmetry, modelCenter); const lookup = structure.lookup3d; - const assembler = Structure.Builder(); + // keep track of added invariant-unit and operator combinations + const added = new Set<string>() + function hash(unit: Unit, oper: SymmetryOperator) { + return `${unit.invariantId}|${oper.name}` + } + + const assembler = Structure.Builder({ label: structure.label }); const { units } = structure; const center = Vec3.zero(); @@ -204,13 +210,17 @@ async function findMatesRadius(ctx: RuntimeContext, structure: Structure, radius for (let uI = 0, _uI = closeUnits.count; uI < _uI; uI++) { const closeUnit = units[closeUnits.indices[uI]]; if (!closeUnit.lookup3d.check(center[0], center[1], center[2], boundingSphere.radius + radius)) continue; - assembler.addWithOperator(unit, oper); + + const h = hash(unit, oper) + if (!added.has(h)) { + assembler.addWithOperator(unit, oper); + added.add(h) + } } } if (ctx.shouldUpdate) await ctx.update('Building symmetry...'); } - return assembler.getStructure(); } diff --git a/src/mol-plugin/index.ts b/src/mol-plugin/index.ts index 87e60e2bdff0e59704a86366ec646bf2d265a1a6..be69bb137c9648150e62f723cc0b4a88f1fe716f 100644 --- a/src/mol-plugin/index.ts +++ b/src/mol-plugin/index.ts @@ -42,6 +42,7 @@ export const DefaultPluginSpec: PluginSpec = { PluginSpec.Action(StateTransforms.Model.TrajectoryFromPDB), PluginSpec.Action(StateTransforms.Model.StructureAssemblyFromModel), PluginSpec.Action(StateTransforms.Model.StructureSymmetryFromModel), + PluginSpec.Action(StateTransforms.Model.StructureSymmetryMatesFromModel), PluginSpec.Action(TransformStructureConformation), PluginSpec.Action(StateTransforms.Model.StructureFromModel), PluginSpec.Action(StateTransforms.Model.StructureFromTrajectory), diff --git a/src/mol-plugin/state/transforms/model.ts b/src/mol-plugin/state/transforms/model.ts index 6f749464e6e05b893cc0b243c58fe879f68915fc..ff10c33a281062ea581e342e57e92a02ea29ff86 100644 --- a/src/mol-plugin/state/transforms/model.ts +++ b/src/mol-plugin/state/transforms/model.ts @@ -40,6 +40,7 @@ export { StructureFromTrajectory }; export { StructureFromModel }; export { StructureAssemblyFromModel }; export { StructureSymmetryFromModel }; +export { StructureSymmetryMatesFromModel }; export { TransformStructureConformation }; export { TransformStructureConformationByMatrix }; export { StructureSelectionFromExpression }; @@ -301,6 +302,31 @@ const StructureSymmetryFromModel = PluginStateTransform.BuiltIn({ } }); +type StructureSymmetryMatesFromModel = typeof StructureSymmetryMatesFromModel +const StructureSymmetryMatesFromModel = PluginStateTransform.BuiltIn({ + name: 'structure-symmetry-mates-from-model', + display: { name: 'Structure Symmetry Mates', description: 'Create molecular structure symmetry mates.' }, + from: SO.Molecule.Model, + to: SO.Molecule.Structure, + params(a) { + return { + radius: PD.Numeric(5), + } + } +})({ + apply({ a, params }, plugin: PluginContext) { + return Task.create('Build Symmetry Mates', async ctx => { + const { radius } = params + const model = a.data; + const base = Structure.ofModel(model); + const s = await StructureSymmetry.builderSymmetryMates(base, radius).runInContext(ctx); + await ensureSecondaryStructure(s) + const props = { label: `Symmetry Mates`, description: structureDesc(s) }; + return new SO.Molecule.Structure(s, props); + }) + } +}); + const _translation = Vec3.zero(), _m = Mat4.zero(), _n = Mat4.zero(); type TransformStructureConformation = typeof TransformStructureConformation const TransformStructureConformation = PluginStateTransform.BuiltIn({