diff --git a/src/mol-math/geometry/spacegroup/construction.ts b/src/mol-math/geometry/spacegroup/construction.ts index a347c60e7307fade102bacacb931788d21f6b4b0..48d9cd359cd97bf77cc52d2ab2b1a8754aca4d25 100644 --- a/src/mol-math/geometry/spacegroup/construction.ts +++ b/src/mol-math/geometry/spacegroup/construction.ts @@ -90,7 +90,7 @@ namespace Spacegroup { export function getSymmetryOperator(spacegroup: Spacegroup, index: number, i: number, j: number, k: number): SymmetryOperator { const operator = updateOperatorMatrix(spacegroup, index, i, j, k, Mat4.zero()); - return SymmetryOperator.create(`${index + 1}_${5 + i}${5 + j}${5 + k}`, operator, { id: '', operList: [] }, '', Vec3.create(i, j, k)); + return SymmetryOperator.create(`${index + 1}_${5 + i}${5 + j}${5 + k}`, operator, { id: '', operList: [] }, '', Vec3.create(i, j, k), index); } function getOperatorMatrix(ids: number[]) { diff --git a/src/mol-math/geometry/symmetry-operator.ts b/src/mol-math/geometry/symmetry-operator.ts index e513259dca683f6bac349ddbe8dc76f831e7831d..4ee54d3537f7fc69025bfd05888612e65ad442b4 100644 --- a/src/mol-math/geometry/symmetry-operator.ts +++ b/src/mol-math/geometry/symmetry-operator.ts @@ -6,6 +6,7 @@ import { Vec3, Mat4, Mat3, Quat } from '../linear-algebra/3d' import { lerp as scalar_lerp } from '../../mol-math/interpolate'; +import { defaults } from '../../mol-util'; interface SymmetryOperator { readonly name: string, @@ -21,6 +22,8 @@ interface SymmetryOperator { readonly ncsId: string, readonly hkl: Vec3, + /** spacegroup symmetry operator index, -1 if not applicable */ + readonly spgrOp: number, readonly matrix: Mat4, // cache the inverse of the transform @@ -35,12 +38,13 @@ namespace SymmetryOperator { export const RotationTranslationEpsilon = 0.005; - export function create(name: string, matrix: Mat4, assembly: SymmetryOperator['assembly'], ncsId?: string, hkl?: Vec3): SymmetryOperator { + export function create(name: string, matrix: Mat4, assembly: SymmetryOperator['assembly'], ncsId?: string, hkl?: Vec3, spgrOp?: number): SymmetryOperator { const _hkl = hkl ? Vec3.clone(hkl) : Vec3.zero(); + spgrOp = defaults(spgrOp, -1) ncsId = ncsId || '' - if (Mat4.isIdentity(matrix)) return { name, assembly, matrix, inverse: Mat4.identity(), isIdentity: true, hkl: _hkl, ncsId }; + if (Mat4.isIdentity(matrix)) return { name, assembly, matrix, inverse: Mat4.identity(), isIdentity: true, hkl: _hkl, spgrOp, ncsId }; if (!Mat4.isRotationAndTranslation(matrix, RotationTranslationEpsilon)) throw new Error(`Symmetry operator (${name}) must be a composition of rotation and translation.`); - return { name, assembly, matrix, inverse: Mat4.invert(Mat4.zero(), matrix), isIdentity: false, hkl: _hkl, ncsId }; + return { name, assembly, matrix, inverse: Mat4.invert(Mat4.zero(), matrix), isIdentity: false, hkl: _hkl, spgrOp, ncsId }; } export function checkIfRotationAndTranslation(rot: Mat3, offset: Vec3) { @@ -106,11 +110,11 @@ namespace SymmetryOperator { /** * Apply the 1st and then 2nd operator. ( = second.matrix * first.matrix). - * Keep `name`, `assembly`, `ncsId` and `hkl` properties from second. + * Keep `name`, `assembly`, `ncsId`, `hkl` and `spgrOpId` properties from second. */ export function compose(first: SymmetryOperator, second: SymmetryOperator) { const matrix = Mat4.mul(Mat4.zero(), second.matrix, first.matrix); - return create(second.name, matrix, second.assembly, second.ncsId, second.hkl); + return create(second.name, matrix, second.assembly, second.ncsId, second.hkl, second.spgrOp); } export interface CoordinateMapper<T extends number> { (index: T, slot: Vec3): Vec3 } diff --git a/src/mol-model/structure/structure/properties.ts b/src/mol-model/structure/structure/properties.ts index 70a449b21cd13c5007377488bae78ec4929b7053..17aa1c75a40ec49304eb821a16f9159c912e2bcc 100644 --- a/src/mol-model/structure/structure/properties.ts +++ b/src/mol-model/structure/structure/properties.ts @@ -113,6 +113,8 @@ const entity = { const unit = { id: StructureElement.property(l => l.unit.id), operator_name: StructureElement.property(l => l.unit.conformation.operator.name), + hkl: StructureElement.property(l => l.unit.conformation.operator.hkl), + spgrOp: StructureElement.property(l => l.unit.conformation.operator.spgrOp), model_num: StructureElement.property(l => l.unit.model.modelNum), pdbx_struct_assembly_id: StructureElement.property(l => l.unit.conformation.operator.assembly.id), diff --git a/src/mol-model/structure/structure/symmetry.ts b/src/mol-model/structure/structure/symmetry.ts index cb9d8ebf34dfdecd269468db5bccdfa30185064a..5379e19368273a32d207bbd2ff17e9fc6dcc28f8 100644 --- a/src/mol-model/structure/structure/symmetry.ts +++ b/src/mol-model/structure/structure/symmetry.ts @@ -117,7 +117,7 @@ function getOperators(symmetry: ModelSymmetry, ijkMin: Vec3, ijkMax: Vec3) { for (let u = 0; u < ncsCount; ++u) { const ncsOp = ncsOperators![u] const matrix = Mat4.mul(Mat4.zero(), symOp.matrix, ncsOp.matrix) - const operator = SymmetryOperator.create(`${symOp.name} ${ncsOp.name}`, matrix, symOp.assembly, ncsOp.ncsId, symOp.hkl); + const operator = SymmetryOperator.create(`${symOp.name} ${ncsOp.name}`, matrix, symOp.assembly, ncsOp.ncsId, symOp.hkl, symOp.spgrOp); operators[operators.length] = operator; } } else { diff --git a/src/mol-plugin/ui/sequence.tsx b/src/mol-plugin/ui/sequence.tsx index b6855b9f569fd2fe06140cb333ac7b80ecff497d..b8f9569f5516dceb2cebca18f877f385237f12ad 100644 --- a/src/mol-plugin/ui/sequence.tsx +++ b/src/mol-plugin/ui/sequence.tsx @@ -18,8 +18,12 @@ import { MarkerAction } from '../../mol-util/marker-action'; import { ParameterControls } from './controls/parameters'; import { ParamDefinition as PD } from '../../mol-util/param-definition'; -function opKey(ids: string[]) { - return ids.sort().join(',') +function opKey(l: StructureElement) { + const ids = SP.unit.pdbx_struct_oper_list_ids(l) + const ncs = SP.unit.struct_ncs_oper_id(l) + const hkl = SP.unit.hkl(l) + const spgrOp = SP.unit.spgrOp(l) + return `${ids.sort().join(',')}|${ncs}|${hkl}|${spgrOp}` } function getSequenceWrapper(state: SequenceViewState, structureSelection: StructureElementSelectionManager): SequenceWrapper.Any | undefined { @@ -32,7 +36,7 @@ function getSequenceWrapper(state: SequenceViewState, structureSelection: Struct StructureElement.set(l, unit, unit.elements[0]) if (SP.entity.id(l) !== entity) continue if (SP.chain.label_asym_id(l) !== chain) continue - if (opKey(SP.unit.pdbx_struct_oper_list_ids(l)) !== operator) continue + if (opKey(l) !== operator) continue // console.log('new PolymerSequenceWrapper', structureSelection.get(structure)) const sw = new PolymerSequenceWrapper({ structure, unit }) @@ -96,10 +100,10 @@ function getOperatorOptions(structure: Structure, entityId: string, label_asym_i if (SP.entity.id(l) !== entityId) return if (SP.chain.label_asym_id(l) !== label_asym_id) return - const id = opKey(SP.unit.pdbx_struct_oper_list_ids(l)) + const id = opKey(l) if (seen.has(id)) return - const label = `${SP.unit.pdbx_struct_oper_list_ids(l).join(', ')}` + const label = unit.conformation.operator.name options.push([ id, label ]) seen.add(id) })