diff --git a/src/mol-model/structure/structure/util/nucleotide.ts b/src/mol-model/structure/structure/util/nucleotide.ts index 8dbb668b74a5330781041f33c30c7d3d23713351..0b77d8301c3bcdd4821a7720dd556fa0cecf4ecf 100644 --- a/src/mol-model/structure/structure/util/nucleotide.ts +++ b/src/mol-model/structure/structure/util/nucleotide.ts @@ -6,15 +6,13 @@ import { Unit, ElementIndex } from 'mol-model/structure'; import { Segmentation, SortedArray } from 'mol-data/int'; -import { isNucleic, MoleculeType } from 'mol-model/structure/model/types'; -import { getElementIndexForAtomRole } from 'mol-model/structure/util'; +import { isNucleic } from 'mol-model/structure/model/types'; export function getNucleotideElements(unit: Unit.Atomic) { const indices: ElementIndex[] = [] const { elements, model } = unit - const { chemicalComponentMap } = model.properties - const { chainAtomSegments, residueAtomSegments, residues } = model.atomicHierarchy - const { label_comp_id } = residues + const { chainAtomSegments, residueAtomSegments } = model.atomicHierarchy + const { moleculeType, traceElementIndex } = model.atomicHierarchy.derived.residue const chainIt = Segmentation.transientSegments(chainAtomSegments, elements) const residueIt = Segmentation.transientSegments(residueAtomSegments, elements) while (chainIt.hasNext) { @@ -22,11 +20,9 @@ export function getNucleotideElements(unit: Unit.Atomic) { while (residueIt.hasNext) { const { index } = residueIt.move(); - const cc = chemicalComponentMap.get(label_comp_id.value(index)) - const moleculeType = cc ? cc.moleculeType : MoleculeType.unknown - if (isNucleic(moleculeType)) { - const elementIndex = getElementIndexForAtomRole(model, index, 'trace') + if (isNucleic(moleculeType[index])) { + const elementIndex = traceElementIndex[index] indices.push(elementIndex === -1 ? residueAtomSegments.offsets[index] : elementIndex) } } diff --git a/src/mol-model/structure/structure/util/polymer.ts b/src/mol-model/structure/structure/util/polymer.ts index a03621f416fa50f264761577f5822fe57be44e30..951e74807430044b6ef8fd5903cd2dbb22710c0d 100644 --- a/src/mol-model/structure/structure/util/polymer.ts +++ b/src/mol-model/structure/structure/util/polymer.ts @@ -7,12 +7,12 @@ import { Unit, ElementIndex } from 'mol-model/structure'; import { Segmentation, OrderedSet, Interval, SortedArray } from 'mol-data/int'; import SortedRanges from 'mol-data/int/sorted-ranges'; -import { getElementIndexForAtomRole } from 'mol-model/structure/util'; export function getAtomicPolymerElements(unit: Unit.Atomic) { const indices: ElementIndex[] = [] const { elements, model } = unit const { residueAtomSegments } = unit.model.atomicHierarchy + const { traceElementIndex } = model.atomicHierarchy.derived.residue const polymerIt = SortedRanges.transientSegments(unit.model.atomicHierarchy.polymerRanges, elements) const residueIt = Segmentation.transientSegments(residueAtomSegments, elements) while (polymerIt.hasNext) { @@ -22,7 +22,7 @@ export function getAtomicPolymerElements(unit: Unit.Atomic) { const residueSegment = residueIt.move() const { start, end, index } = residueSegment if (OrderedSet.areIntersecting(Interval.ofRange(elements[start], elements[end - 1]), elements)) { - const elementIndex = getElementIndexForAtomRole(model, index, 'trace') + const elementIndex = traceElementIndex[index] indices.push(elementIndex === -1 ? residueAtomSegments.offsets[index] : elementIndex) } } @@ -47,13 +47,14 @@ export function getAtomicGapElements(unit: Unit.Atomic) { const indices: ElementIndex[] = [] const { elements, model, residueIndex } = unit const { residueAtomSegments } = unit.model.atomicHierarchy + const { traceElementIndex } = model.atomicHierarchy.derived.residue const gapIt = SortedRanges.transientSegments(unit.model.atomicHierarchy.gapRanges, unit.elements); while (gapIt.hasNext) { const gapSegment = gapIt.move(); const indexStart = residueIndex[elements[gapSegment.start]] const indexEnd = residueIndex[elements[gapSegment.end - 1]] - const elementIndexStart = getElementIndexForAtomRole(model, indexStart, 'trace') - const elementIndexEnd = getElementIndexForAtomRole(model, indexEnd, 'trace') + const elementIndexStart = traceElementIndex[indexStart] + const elementIndexEnd = traceElementIndex[indexEnd] indices.push(elementIndexStart === -1 ? residueAtomSegments.offsets[indexStart] : elementIndexStart) indices.push(elementIndexEnd === -1 ? residueAtomSegments.offsets[indexEnd] : elementIndexEnd) diff --git a/src/mol-model/structure/util.ts b/src/mol-model/structure/util.ts index ab709c2b7ced014e8c13886bbd56669cfb94f773..e688b99c2c88325f541e5184020b98dd5648bcd8 100644 --- a/src/mol-model/structure/util.ts +++ b/src/mol-model/structure/util.ts @@ -49,11 +49,6 @@ export function getAtomIdForAtomRole(moleculeType: MoleculeType, atomRole: AtomR return '' } -export function getElementIndexForAtomRole(model: Model, rI: ResidueIndex, atomRole: AtomRole) { - const atomId = getAtomIdForAtomRole(getAtomicMoleculeType(model, rI), atomRole) - return model.atomicHierarchy.index.findAtomOnResidue(rI, atomId) -} - export function residueLabel(model: Model, rI: number) { const { residues, chains, residueAtomSegments, chainAtomSegments } = model.atomicHierarchy const { label_comp_id, label_seq_id } = residues diff --git a/src/mol-repr/structure/visual/nucleotide-block-mesh.ts b/src/mol-repr/structure/visual/nucleotide-block-mesh.ts index 2cf7c0b2db22fd067ccb1dc3fba27670898e0d50..5c5843764c01d13c2bcb27a9dfc60423bdeb9c7a 100644 --- a/src/mol-repr/structure/visual/nucleotide-block-mesh.ts +++ b/src/mol-repr/structure/visual/nucleotide-block-mesh.ts @@ -8,8 +8,7 @@ import { Unit, Structure, ElementIndex } from 'mol-model/structure'; import { UnitsVisual } from '../representation'; import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { Segmentation } from 'mol-data/int'; -import { MoleculeType, isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types'; -import { getElementIndexForAtomRole } from 'mol-model/structure/util'; +import { isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types'; import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual'; import { NucleotideLocationIterator, markNucleotideElement, getNucleotideElementLoci } from './util/nucleotide'; import { ParamDefinition as PD } from 'mol-util/param-definition'; @@ -20,6 +19,7 @@ import { addCylinder } from 'mol-geo/geometry/mesh/builder/cylinder'; import { VisualContext } from 'mol-repr/representation'; import { Theme } from 'mol-theme/theme'; import { VisualUpdateState } from 'mol-repr/util'; +import { CylinderProps } from 'mol-geo/primitive/cylinder'; const p1 = Vec3.zero() const p2 = Vec3.zero() @@ -37,6 +37,7 @@ const box = Box() export const NucleotideBlockMeshParams = { sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }), + radialSegments: PD.Numeric(16, { min: 3, max: 56, step: 1 }), } export const DefaultNucleotideBlockMeshProps = PD.getDefaultValues(NucleotideBlockMeshParams) export type NucleotideBlockMeshProps = typeof DefaultNucleotideBlockMeshProps @@ -44,31 +45,35 @@ export type NucleotideBlockMeshProps = typeof DefaultNucleotideBlockMeshProps function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: NucleotideBlockMeshProps, mesh?: Mesh) { if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh) - const { sizeFactor } = props + const nucleotideElementCount = unit.nucleotideElements.length + if (!nucleotideElementCount) return Mesh.createEmpty(mesh) - // TODO better vertex count estimate - const builder = MeshBuilder.create(256, 128, mesh) + const { sizeFactor, radialSegments } = props + + const vertexCount = nucleotideElementCount * (box.vertices.length / 3 + radialSegments * 2) + const builder = MeshBuilder.create(vertexCount, vertexCount / 4, mesh) const { elements, model } = unit - const { chemicalComponentMap, modifiedResidues } = model.properties + const { modifiedResidues } = model.properties const { chainAtomSegments, residueAtomSegments, residues, index: atomicIndex } = model.atomicHierarchy + const { moleculeType, traceElementIndex } = model.atomicHierarchy.derived.residue const { label_comp_id } = residues const pos = unit.conformation.invariantPosition const chainIt = Segmentation.transientSegments(chainAtomSegments, elements) const residueIt = Segmentation.transientSegments(residueAtomSegments, elements) + const cylinderProps: CylinderProps = { radiusTop: 1 * sizeFactor, radiusBottom: 1 * sizeFactor, radialSegments } + let i = 0 while (chainIt.hasNext) { residueIt.setSegment(chainIt.move()); while (residueIt.hasNext) { const { index: residueIndex } = residueIt.move(); - let compId = label_comp_id.value(residueIndex) - const cc = chemicalComponentMap.get(compId) - const moleculeType = cc ? cc.moleculeType : MoleculeType.unknown - if (isNucleic(moleculeType)) { + if (isNucleic(moleculeType[residueIndex])) { + let compId = label_comp_id.value(residueIndex) const parentId = modifiedResidues.parentId.get(compId) if (parentId !== undefined) compId = parentId let idx1: ElementIndex | -1 = -1, idx2: ElementIndex | -1 = -1, idx3: ElementIndex | -1 = -1, idx4: ElementIndex | -1 = -1, idx5: ElementIndex | -1 = -1, idx6: ElementIndex | -1 = -1 @@ -81,7 +86,7 @@ function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structure: St idx3 = atomicIndex.findAtomOnResidue(residueIndex, 'C6') idx4 = atomicIndex.findAtomOnResidue(residueIndex, 'C2') idx5 = atomicIndex.findAtomOnResidue(residueIndex, 'N9') - idx6 = getElementIndexForAtomRole(model, residueIndex, 'trace') + idx6 = traceElementIndex[residueIndex] } else if (isPyrimidineBase(compId)) { height = 3.0 idx1 = atomicIndex.findAtomOnResidue(residueIndex, 'N3') @@ -89,13 +94,13 @@ function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structure: St idx3 = atomicIndex.findAtomOnResidue(residueIndex, 'C4') idx4 = atomicIndex.findAtomOnResidue(residueIndex, 'C2') idx5 = atomicIndex.findAtomOnResidue(residueIndex, 'N1') - idx6 = getElementIndexForAtomRole(model, residueIndex, 'trace') + idx6 = traceElementIndex[residueIndex] } if (idx5 !== -1 && idx6 !== -1) { pos(idx5, p5); pos(idx6, p6) builder.setGroup(i) - addCylinder(builder, p5, p6, 1, { radiusTop: 1 * sizeFactor, radiusBottom: 1 * sizeFactor }) + addCylinder(builder, p5, p6, 1, cylinderProps) if (idx1 !== -1 && idx2 !== -1 && idx3 !== -1 && idx4 !== -1) { pos(idx1, p1); pos(idx2, p2); pos(idx3, p3); pos(idx4, p4); Vec3.normalize(v12, Vec3.sub(v12, p2, p1)) diff --git a/src/mol-repr/structure/visual/util/nucleotide.ts b/src/mol-repr/structure/visual/util/nucleotide.ts index 2214d4d646aca733b282fcf6fe8271784e09e93c..44ce2c3e8638cb616ec6aed922d9565e1aed2617 100644 --- a/src/mol-repr/structure/visual/util/nucleotide.ts +++ b/src/mol-repr/structure/visual/util/nucleotide.ts @@ -12,7 +12,6 @@ import { LocationIterator } from 'mol-geo/util/location-iterator'; import { PickingId } from 'mol-geo/geometry/picking'; import { StructureGroup } from 'mol-repr/structure/units-visual'; import { getResidueLoci } from './common'; -import { getElementIndexForAtomRole } from 'mol-model/structure/util'; export namespace NucleotideLocationIterator { export function fromGroup(group: Unit.SymmetryGroup): LocationIterator { @@ -50,6 +49,7 @@ export function markNucleotideElement(loci: Loci, structureGroup: StructureGroup if (!Unit.isAtomic(unit)) return false const { nucleotideElements, model, elements } = unit const { index, offsets } = model.atomicHierarchy.residueAtomSegments + const { traceElementIndex } = model.atomicHierarchy.derived.residue const groupCount = nucleotideElements.length for (const e of loci.elements) { const unitIdx = group.unitIndexMap.get(e.unit.id) @@ -61,8 +61,8 @@ export function markNucleotideElement(loci: Loci, structureGroup: StructureGroup const unitIndexMin = OrderedSet.findPredecessorIndex(elements, offsets[rI]) const unitIndexMax = OrderedSet.findPredecessorIndex(elements, offsets[rI + 1] - 1) const unitIndexInterval = Interval.ofRange(unitIndexMin, unitIndexMax) - if(!OrderedSet.isSubset(e.indices, unitIndexInterval)) return - const eI = getElementIndexForAtomRole(model, rI, 'trace') + if (!OrderedSet.isSubset(e.indices, unitIndexInterval)) return + const eI = traceElementIndex[rI] const idx = OrderedSet.indexOf(eUnit.nucleotideElements, eI) if (idx !== -1) { if (apply(Interval.ofSingleton(unitIdx * groupCount + idx))) changed = true diff --git a/src/mol-repr/structure/visual/util/polymer.ts b/src/mol-repr/structure/visual/util/polymer.ts index 11e2f8c61a8e41830e5111d73387baad74458d6f..288bf8527c97cf1344c9de45a6eccfacccf7d473 100644 --- a/src/mol-repr/structure/visual/util/polymer.ts +++ b/src/mol-repr/structure/visual/util/polymer.ts @@ -11,7 +11,6 @@ import { EmptyLoci, Loci } from 'mol-model/loci'; import { LocationIterator } from 'mol-geo/util/location-iterator'; import { PickingId } from 'mol-geo/geometry/picking'; import { StructureGroup } from 'mol-repr/structure/units-visual'; -import { getElementIndexForAtomRole } from 'mol-model/structure/util'; import { getResidueLoci } from './common'; export * from './polymer/backbone-iterator' @@ -86,6 +85,7 @@ export function markPolymerElement(loci: Loci, structureGroup: StructureGroup, a if (loci.structure !== structure) return false const { polymerElements, model, elements } = group.units[0] const { index, offsets } = model.atomicHierarchy.residueAtomSegments + const { traceElementIndex } = model.atomicHierarchy.derived.residue const groupCount = polymerElements.length for (const e of loci.elements) { const unitIdx = group.unitIndexMap.get(e.unit.id) @@ -97,7 +97,7 @@ export function markPolymerElement(loci: Loci, structureGroup: StructureGroup, a const unitIndexMax = OrderedSet.findPredecessorIndex(elements, offsets[rI + 1] - 1) const unitIndexInterval = Interval.ofRange(unitIndexMin, unitIndexMax) if (!OrderedSet.isSubset(e.indices, unitIndexInterval)) return - const eI = getElementIndexForAtomRole(model, rI, 'trace') + const eI = traceElementIndex[rI] const idx = OrderedSet.indexOf(e.unit.polymerElements, eI) if (idx !== -1) { if (apply(Interval.ofSingleton(unitIdx * groupCount + idx))) changed = true diff --git a/src/mol-repr/structure/visual/util/polymer/backbone-iterator.ts b/src/mol-repr/structure/visual/util/polymer/backbone-iterator.ts index 31170313fb9d345db914146b927b1650c2a38a14..f80b306af030cd83547bfa2cf82fb88f470ca0d1 100644 --- a/src/mol-repr/structure/visual/util/polymer/backbone-iterator.ts +++ b/src/mol-repr/structure/visual/util/polymer/backbone-iterator.ts @@ -8,9 +8,7 @@ import { Unit, StructureElement, ElementIndex, ResidueIndex } from 'mol-model/st import { Segmentation } from 'mol-data/int'; import Iterator from 'mol-data/iterator'; import SortedRanges from 'mol-data/int/sorted-ranges'; -import { getElementIndexForAtomRole } from 'mol-model/structure/util'; import { getPolymerRanges } from '../polymer'; -import { AtomRole } from 'mol-model/structure/model/types'; /** Iterates over consecutive pairs of residues/coarse elements in polymers */ export function PolymerBackboneIterator(unit: Unit): Iterator<PolymerBackbonePair> { @@ -37,6 +35,7 @@ function createPolymerBackbonePair (unit: Unit) { const enum AtomicPolymerBackboneIteratorState { nextPolymer, firstResidue, nextResidue, cycle } export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePair> { + private traceElementIndex: ArrayLike<ElementIndex> private value: PolymerBackbonePair private polymerIt: SortedRanges.Iterator<ElementIndex, ResidueIndex> private residueIt: Segmentation.SegmentIterator<ResidueIndex> @@ -44,19 +43,13 @@ export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePa private residueSegment: Segmentation.Segment<ResidueIndex> hasNext: boolean = false; - private getElementIndex(residueIndex: ResidueIndex, atomRole: AtomRole) { - const { atomicHierarchy } = this.unit.model - const elementIndex = getElementIndexForAtomRole(this.unit.model, residueIndex, atomRole) - return elementIndex === -1 ? atomicHierarchy.residueAtomSegments.offsets[residueIndex] : elementIndex - } - move() { if (this.state === AtomicPolymerBackboneIteratorState.nextPolymer) { while (this.polymerIt.hasNext) { this.residueIt.setSegment(this.polymerIt.move()); if (this.residueIt.hasNext) { this.residueSegment = this.residueIt.move() - this.value.centerB.element = this.getElementIndex(this.residueSegment.index, 'trace') + this.value.centerB.element = this.traceElementIndex[this.residueSegment.index] this.state = AtomicPolymerBackboneIteratorState.nextResidue break } @@ -66,7 +59,7 @@ export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePa if (this.state === AtomicPolymerBackboneIteratorState.nextResidue) { this.residueSegment = this.residueIt.move() this.value.centerA.element = this.value.centerB.element - this.value.centerB.element = this.getElementIndex(this.residueSegment.index, 'trace') + this.value.centerB.element = this.traceElementIndex[this.residueSegment.index] if (!this.residueIt.hasNext) { if (this.unit.model.atomicHierarchy.cyclicPolymerMap.has(this.residueSegment.index)) { this.state = AtomicPolymerBackboneIteratorState.cycle @@ -78,7 +71,7 @@ export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePa } else if (this.state === AtomicPolymerBackboneIteratorState.cycle) { const { cyclicPolymerMap } = this.unit.model.atomicHierarchy this.value.centerA.element = this.value.centerB.element - this.value.centerB.element = this.getElementIndex(cyclicPolymerMap.get(this.residueSegment.index)!, 'trace') + this.value.centerB.element = this.traceElementIndex[cyclicPolymerMap.get(this.residueSegment.index)!] // TODO need to advance to a polymer that has two or more residues (can't assume it has) this.state = AtomicPolymerBackboneIteratorState.nextPolymer } @@ -88,6 +81,7 @@ export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePa } constructor(private unit: Unit.Atomic) { + this.traceElementIndex = unit.model.atomicHierarchy.derived.residue.traceElementIndex this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements) this.residueIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements) this.value = createPolymerBackbonePair(unit) diff --git a/src/mol-repr/structure/visual/util/polymer/gap-iterator.ts b/src/mol-repr/structure/visual/util/polymer/gap-iterator.ts index 663f10162596a611ff62efc0c4811a5508e99255..bc15bb9f5e71df6d78ece3a9d42541fabcb45f90 100644 --- a/src/mol-repr/structure/visual/util/polymer/gap-iterator.ts +++ b/src/mol-repr/structure/visual/util/polymer/gap-iterator.ts @@ -5,10 +5,8 @@ */ import { Unit, StructureElement, ElementIndex, ResidueIndex } from 'mol-model/structure'; -import { AtomRole } from 'mol-model/structure/model/types'; import Iterator from 'mol-data/iterator'; import SortedRanges from 'mol-data/int/sorted-ranges'; -import { getElementIndexForAtomRole } from 'mol-model/structure/util'; import { getGapRanges } from '../polymer'; /** Iterates over gaps, i.e. the stem residues/coarse elements adjacent to gaps */ @@ -34,25 +32,22 @@ function createPolymerGapPair (unit: Unit) { } export class AtomicPolymerGapIterator implements Iterator<PolymerGapPair> { + private traceElementIndex: ArrayLike<ElementIndex> private value: PolymerGapPair private gapIt: SortedRanges.Iterator<ElementIndex, ResidueIndex> hasNext: boolean = false; - private getElementIndex(residueIndex: ResidueIndex, atomRole: AtomRole) { - const elementIndex = getElementIndexForAtomRole(this.unit.model, residueIndex, atomRole) - return elementIndex === -1 ? this.unit.model.atomicHierarchy.residueAtomSegments.offsets[residueIndex] : elementIndex - } - move() { const { elements, residueIndex } = this.unit const gapSegment = this.gapIt.move(); - this.value.centerA.element = this.getElementIndex(residueIndex[elements[gapSegment.start]], 'trace') - this.value.centerB.element = this.getElementIndex(residueIndex[elements[gapSegment.end - 1]], 'trace') + this.value.centerA.element = this.traceElementIndex[residueIndex[elements[gapSegment.start]]] + this.value.centerB.element = this.traceElementIndex[residueIndex[elements[gapSegment.end - 1]]] this.hasNext = this.gapIt.hasNext return this.value; } constructor(private unit: Unit.Atomic) { + this.traceElementIndex = unit.model.atomicHierarchy.derived.residue.traceElementIndex this.gapIt = SortedRanges.transientSegments(getGapRanges(unit), unit.elements); this.value = createPolymerGapPair(unit) this.hasNext = this.gapIt.hasNext