diff --git a/data/mmcif-field-names.csv b/data/mmcif-field-names.csv index 1d629c1fa923ee133a80caebaf4dc1e805c82b3d..68893eed5925e0a3543214d53e3e07cb51211aa9 100644 --- a/data/mmcif-field-names.csv +++ b/data/mmcif-field-names.csv @@ -66,8 +66,8 @@ entity_poly.entity_id entity_poly.type entity_poly.nstd_linkage entity_poly.nstd_monomer -entity_poly.pdbx_seq_one_letter_code -entity_poly.pdbx_seq_one_letter_code_can +entity_poly.pdbx_seq_one_letter_code +entity_poly.pdbx_seq_one_letter_code_can entity_poly.pdbx_strand_id entity_poly.pdbx_target_identifier diff --git a/src/mol-geo/representation/util.ts b/src/mol-geo/representation/util.ts index 407ade0f2311b0472fd15688a57b63b555251444..b16444f081f7431283f8807e3aab802f1d388953 100644 --- a/src/mol-geo/representation/util.ts +++ b/src/mol-geo/representation/util.ts @@ -22,7 +22,8 @@ export function getQualityProps(props: Partial<QualityProps>, structure?: Struct let linearSegments = defaults(props.linearSegments, 8) if (quality === 'auto' && structure) { - const score = structure.elementCount + let score = structure.elementCount + if (structure.isCoarse) score *= 10 if (score > 500_000) { quality = 'lowest' } else if (score > 100_000) { diff --git a/src/mol-model/structure/model/properties/utils/atomic-ranges.ts b/src/mol-model/structure/model/properties/utils/atomic-ranges.ts index 52db07fc11d0cb33654db16bef3e6ddb83b52713..7853fb37776f4f9abc625c332e08b55883357a20 100644 --- a/src/mol-model/structure/model/properties/utils/atomic-ranges.ts +++ b/src/mol-model/structure/model/properties/utils/atomic-ranges.ts @@ -22,13 +22,13 @@ function getMoleculeType(compId: string, chemicalComponentMap: ChemicalComponent return cc ? cc.moleculeType : MoleculeType.unknown } -function getElementIndexForAtomId(rI: ResidueIndex, atomId: string, data: AtomicData, segments: AtomicSegments, ): ElementIndex { +function getElementIndexForAtomId(rI: ResidueIndex, atomId: string, data: AtomicData, segments: AtomicSegments): ElementIndex { const { offsets } = segments.residueAtomSegments const { label_atom_id } = data.atoms for (let j = offsets[rI], _j = offsets[rI + 1]; j < _j; j++) { - if (label_atom_id.value(j) === atomId) return j as ElementIndex + if (label_atom_id.value(j) === atomId) return j } - return offsets[rI] as ElementIndex + return offsets[rI] } function areBackboneConnected(riStart: ResidueIndex, riEnd: ResidueIndex, data: AtomicData, segments: AtomicSegments, conformation: AtomicConformation, chemicalComponentMap: ChemicalComponentMap) { @@ -46,7 +46,7 @@ function areBackboneConnected(riStart: ResidueIndex, riEnd: ResidueIndex, data: const { x, y, z } = conformation const pStart = Vec3.create(x[eiStart], y[eiStart], z[eiStart]) const pEnd = Vec3.create(x[eiEnd], y[eiEnd], z[eiEnd]) - return Vec3.distance(pStart, pEnd) < 2 + return Vec3.distance(pStart, pEnd) < 10 } export function getAtomicRanges(data: AtomicData, segments: AtomicSegments, conformation: AtomicConformation, chemicalComponentMap: ChemicalComponentMap): AtomicRanges { @@ -90,6 +90,13 @@ export function getAtomicRanges(data: AtomicData, segments: AtomicSegments, conf startIndex = residueSegment.start } else if (!residueIt.hasNext) { polymerRanges.push(startIndex, residueSegment.end - 1) + } else { + const riStart = segments.residueAtomSegments.index[residueSegment.start] + const riEnd = segments.residueAtomSegments.index[prevEnd - 1] + if (!areBackboneConnected(riStart, riEnd, data, segments, conformation, chemicalComponentMap)) { + polymerRanges.push(startIndex, prevEnd - 1) + startIndex = residueSegment.start + } } } else { startIndex = residueSegment.start // start polymer diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts index e22d4b3fba06f57d2ecd2d6dd101ec171e2bfbad..b93f7e96c4edb81c001556a6681763f2b19490ab 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -40,7 +40,8 @@ class Structure { models?: ReadonlyArray<Model>, hashCode: number, elementCount: number, - } = { hashCode: -1, elementCount: 0 }; + polymerResidueCount: number, + } = { hashCode: -1, elementCount: 0, polymerResidueCount: 0 }; subsetBuilder(isSorted: boolean) { return new StructureSubsetBuilder(this, isSorted); @@ -51,6 +52,18 @@ class Structure { return this._props.elementCount; } + /** Count of all polymer residues in the structure */ + get polymerResidueCount() { + return this._props.polymerResidueCount; + } + + /** Coarse structure, defined as Containing less than twice as many elements as polymer residues */ + get isCoarse() { + const ec = this.elementCount + const prc = this.polymerResidueCount + return prc && ec ? ec / prc < 2 : false + } + get hashCode() { if (this._props.hashCode !== -1) return this._props.hashCode; return this.computeHash(); @@ -123,12 +136,14 @@ class Structure { constructor(units: ArrayLike<Unit>) { const map = IntMap.Mutable<Unit>(); let elementCount = 0; + let polymerResidueCount = 0; let isSorted = true; let lastId = units.length > 0 ? units[0].id : 0; for (let i = 0, _i = units.length; i < _i; i++) { const u = units[i]; map.set(u.id, u); elementCount += u.elements.length; + polymerResidueCount += u.polymerElements.length; if (u.id < lastId) isSorted = false; lastId = u.id; } @@ -136,6 +151,7 @@ class Structure { this.unitMap = map; this.units = units as ReadonlyArray<Unit>; this._props.elementCount = elementCount; + this._props.polymerResidueCount = polymerResidueCount; } } diff --git a/src/mol-model/structure/structure/util/polymer.ts b/src/mol-model/structure/structure/util/polymer.ts index e3e9825b4e8f674e1be14efddbeeed2ec77c01e3..a03621f416fa50f264761577f5822fe57be44e30 100644 --- a/src/mol-model/structure/structure/util/polymer.ts +++ b/src/mol-model/structure/structure/util/polymer.ts @@ -21,7 +21,7 @@ export function getAtomicPolymerElements(unit: Unit.Atomic) { while (residueIt.hasNext) { const residueSegment = residueIt.move() const { start, end, index } = residueSegment - if (OrderedSet.areIntersecting(Interval.ofBounds(elements[start], elements[end - 1]), elements)) { + if (OrderedSet.areIntersecting(Interval.ofRange(elements[start], elements[end - 1]), elements)) { const elementIndex = getElementIndexForAtomRole(model, index, 'trace') indices.push(elementIndex === -1 ? residueAtomSegments.offsets[index] : elementIndex) }