diff --git a/src/mol-plugin/ui/sequence.tsx b/src/mol-plugin/ui/sequence.tsx index 234ba264bf098d87b9164f5f0103f0c8d5720416..e61635b3b20f050273143227cd1849fe586b0330 100644 --- a/src/mol-plugin/ui/sequence.tsx +++ b/src/mol-plugin/ui/sequence.tsx @@ -18,8 +18,10 @@ import { ParameterControls } from './controls/parameters'; import { ParamDefinition as PD } from '../../mol-util/param-definition'; import { HeteroSequenceWrapper } from './sequence/hetero'; import { State, StateSelection } from '../../mol-state'; +import { ChainSequenceWrapper } from './sequence/chain'; +import { ElementSequenceWrapper } from './sequence/element'; -const MaxDisplaySequenceLength = 10000 +const MaxDisplaySequenceLength = 5000 function opKey(l: StructureElement.Location) { const ids = SP.unit.pdbx_struct_oper_list_ids(l) @@ -45,19 +47,33 @@ function getSequenceWrapper(state: SequenceViewState, structureSelection: Struct if (unit.invariantId !== invariantUnitId) continue if (opKey(l) !== operatorKey) continue + const data = { structure, unit } + + let sw: SequenceWrapper<any> if (unit.polymerElements.length) { const l = StructureElement.Location.create(unit, unit.elements[0]) const entitySeq = unit.model.sequence.byEntityKey[SP.entity.key(l)] // check if entity sequence is available - if (!entitySeq) return 'No sequence available' - // check if sequence is too long - if (entitySeq.sequence.length > MaxDisplaySequenceLength) { - return `Sequence too long (${entitySeq.sequence.length} residues)` + if (entitySeq && entitySeq.sequence.length <= MaxDisplaySequenceLength) { + sw = new PolymerSequenceWrapper(data) + } else { + if (Unit.isAtomic(unit) || unit.polymerElements.length > MaxDisplaySequenceLength) { + sw = new ChainSequenceWrapper(data) + } else { + sw = new ElementSequenceWrapper(data) + } + } + } else if (Unit.isAtomic(unit)) { + if (unit.residueCount > MaxDisplaySequenceLength) { + sw = new ChainSequenceWrapper(data) + } else { + sw = new HeteroSequenceWrapper(data) } + } else { + console.warn('should not happen, expecting coarse units to be polymeric') + sw = new ChainSequenceWrapper(data) } - const Wrapper = unit.polymerElements.length ? PolymerSequenceWrapper : HeteroSequenceWrapper - const sw = new Wrapper({ structure, unit }) sw.markResidue(structureSelection.get(structure), MarkerAction.Select) return sw } diff --git a/src/mol-plugin/ui/sequence/chain.ts b/src/mol-plugin/ui/sequence/chain.ts new file mode 100644 index 0000000000000000000000000000000000000000..4fa0dc1d6f923b1bb725ef887462d14d6890bb79 --- /dev/null +++ b/src/mol-plugin/ui/sequence/chain.ts @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Structure, StructureElement, StructureProperties } from '../../../mol-model/structure'; +import { SequenceWrapper, StructureUnit } from './wrapper'; +import { OrderedSet, Interval } from '../../../mol-data/int'; +import { Loci } from '../../../mol-model/loci'; +import { ColorNames } from '../../../mol-util/color/names'; + +export class ChainSequenceWrapper extends SequenceWrapper<StructureUnit> { + private label: string + private indices: Interval<StructureElement.UnitIndex> + private loci: StructureElement.Loci + + residueLabel(seqIdx: number) { + return this.label + } + residueColor(seqIdx: number) { + return ColorNames.black + } + + eachResidue(loci: Loci, apply: (set: OrderedSet) => boolean) { + let changed = false + const { structure, unit } = this.data + if (StructureElement.Loci.is(loci)) { + if (!Structure.areRootsEquivalent(loci.structure, structure)) return false + loci = StructureElement.Loci.remap(loci, structure) + + for (const e of loci.elements) { + if (e.unit.id === unit.id) { + if (OrderedSet.isSubset(this.indices, e.indices)) { + if (apply(Interval.ofSingleton(0))) changed = true + } + } + } + } else if (Structure.isLoci(loci)) { + if (!Structure.areRootsEquivalent(loci.structure, structure)) return false + + if (apply(Interval.ofSingleton(0))) changed = true + } + return changed + } + + getLoci(seqIdx: number) { + return this.loci + } + + constructor(data: StructureUnit) { + const counts: string[] = [] + const l = StructureElement.Location.create(data.unit, data.unit.elements[0]) + const entitySeq = data.unit.model.sequence.byEntityKey[StructureProperties.entity.key(l)] + if (entitySeq) counts.push(`${entitySeq.sequence.length} residues`) + counts.push(`${data.unit.elements.length} elements`) + + const length = 1 + const markerArray = new Uint8Array(length) + + super(data, markerArray, length) + + this.label = `Whole Unit (${counts.join(', ')})` + this.indices = Interval.ofBounds(0, data.unit.elements.length) + this.loci = StructureElement.Loci(this.data.structure, [{ + unit: this.data.unit, indices: this.indices + }]) + } +} \ No newline at end of file diff --git a/src/mol-plugin/ui/sequence/element.ts b/src/mol-plugin/ui/sequence/element.ts new file mode 100644 index 0000000000000000000000000000000000000000..caa0e54c4b0c1839243bd487061609a7bff1d650 --- /dev/null +++ b/src/mol-plugin/ui/sequence/element.ts @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Structure, StructureElement } from '../../../mol-model/structure'; +import { SequenceWrapper, StructureUnit } from './wrapper'; +import { OrderedSet, Interval } from '../../../mol-data/int'; +import { Loci } from '../../../mol-model/loci'; +import { ColorNames } from '../../../mol-util/color/names'; + +export class ElementSequenceWrapper extends SequenceWrapper<StructureUnit> { + private indices: Interval<StructureElement.UnitIndex> + + residueLabel(seqIdx: number) { + return 'X' + } + residueColor(seqIdx: number) { + return ColorNames.black + } + + eachResidue(loci: Loci, apply: (set: OrderedSet) => boolean) { + let changed = false + const { structure, unit } = this.data + if (StructureElement.Loci.is(loci)) { + if (!Structure.areRootsEquivalent(loci.structure, structure)) return false + loci = StructureElement.Loci.remap(loci, structure) + + for (const e of loci.elements) { + if (e.unit.id === unit.id) { + if (OrderedSet.isSubset(this.indices, e.indices)) { + if (apply(e.indices)) changed = true + } + } + } + } else if (Structure.isLoci(loci)) { + if (!Structure.areRootsEquivalent(loci.structure, structure)) return false + + if (apply(this.indices)) changed = true + } + return changed + } + + getLoci(seqIdx: number) { + const elements: StructureElement.Loci['elements'][0][] = [{ + unit: this.data.unit, + indices: Interval.ofSingleton(seqIdx) + }] + return StructureElement.Loci(this.data.structure, elements) + } + + constructor(data: StructureUnit) { + const length = data.unit.elements.length + const markerArray = new Uint8Array(length) + + super(data, markerArray, length) + + this.indices = Interval.ofBounds(0, length) + } +} \ No newline at end of file