Skip to content
Snippets Groups Projects
Commit f10a135d authored by Alexander Rose's avatar Alexander Rose
Browse files

sequence view, better handling of missing residues, full structure loci

parent f300e524
No related branches found
No related tags found
No related merge requests found
...@@ -37,6 +37,7 @@ namespace OrderedSet { ...@@ -37,6 +37,7 @@ namespace OrderedSet {
export const union: <T extends number = number>(a: OrderedSet<T>, b: OrderedSet<T>) => OrderedSet<T> = Base.union as any; export const union: <T extends number = number>(a: OrderedSet<T>, b: OrderedSet<T>) => OrderedSet<T> = Base.union as any;
export const intersect: <T extends number = number>(a: OrderedSet<T>, b: OrderedSet<T>) => OrderedSet<T> = Base.intersect as any; export const intersect: <T extends number = number>(a: OrderedSet<T>, b: OrderedSet<T>) => OrderedSet<T> = Base.intersect as any;
/** Returns elements of `a` that are not in `b`, i.e `a` - `b` */
export const subtract: <T extends number = number>(a: OrderedSet<T>, b: OrderedSet<T>) => OrderedSet<T> = Base.subtract as any; export const subtract: <T extends number = number>(a: OrderedSet<T>, b: OrderedSet<T>) => OrderedSet<T> = Base.subtract as any;
export const findPredecessorIndex: <T extends number = number>(set: OrderedSet<T>, x: number) => number = Base.findPredecessorIndex as any; export const findPredecessorIndex: <T extends number = number>(set: OrderedSet<T>, x: number) => number = Base.findPredecessorIndex as any;
......
...@@ -12,9 +12,9 @@ namespace SortedArray { ...@@ -12,9 +12,9 @@ namespace SortedArray {
export const ofUnsortedArray: <T extends number = number>(xs: ArrayLike<number>) => SortedArray<T> = Impl.ofUnsortedArray as any; export const ofUnsortedArray: <T extends number = number>(xs: ArrayLike<number>) => SortedArray<T> = Impl.ofUnsortedArray as any;
export const ofSingleton: <T extends number = number>(v: number) => SortedArray<T> = Impl.ofSingleton as any; export const ofSingleton: <T extends number = number>(v: number) => SortedArray<T> = Impl.ofSingleton as any;
export const ofSortedArray: <T extends number = number>(xs: ArrayLike<number>) => SortedArray<T> = Impl.ofSortedArray as any; export const ofSortedArray: <T extends number = number>(xs: ArrayLike<number>) => SortedArray<T> = Impl.ofSortedArray as any;
// create sorted array [min, max] (it DOES contain the max value) /** create sorted array [min, max] (it DOES contain the max value) */
export const ofRange: <T extends number = number>(min: T, max: T) => SortedArray<T> = Impl.ofRange as any; export const ofRange: <T extends number = number>(min: T, max: T) => SortedArray<T> = Impl.ofRange as any;
// create sorted array [min, max) (it does NOT contain the max value) /** create sorted array [min, max) (it does NOT contain the max value) */
export const ofBounds: <T extends number = number>(min: T, max: T) => SortedArray<T> = (min, max) => Impl.ofRange(min, max - 1) as any; export const ofBounds: <T extends number = number>(min: T, max: T) => SortedArray<T> = (min, max) => Impl.ofRange(min, max - 1) as any;
export const is: <T extends number = number>(v: any) => v is SortedArray<T> = Impl.is as any; export const is: <T extends number = number>(v: any) => v is SortedArray<T> = Impl.is as any;
...@@ -23,9 +23,9 @@ namespace SortedArray { ...@@ -23,9 +23,9 @@ namespace SortedArray {
export const indexOf: <T extends number = number>(array: SortedArray<T>, x: T) => number = Impl.indexOf as any; export const indexOf: <T extends number = number>(array: SortedArray<T>, x: T) => number = Impl.indexOf as any;
export const indexOfInInterval: <T extends number = number>(array: SortedArray<T>, x: number, bounds: Interval) => number = Impl.indexOfInInterval as any; export const indexOfInInterval: <T extends number = number>(array: SortedArray<T>, x: number, bounds: Interval) => number = Impl.indexOfInInterval as any;
// array[0] /** Returns `array[0]` */
export const start: <T extends number = number>(array: SortedArray<T>) => T = Impl.start as any; export const start: <T extends number = number>(array: SortedArray<T>) => T = Impl.start as any;
// array[array.length - 1] + 1 /** Returns `array[array.length - 1] + 1` */
export const end: <T extends number = number>(array: SortedArray<T>) => T = Impl.end as any; export const end: <T extends number = number>(array: SortedArray<T>) => T = Impl.end as any;
export const min: <T extends number = number>(array: SortedArray<T>) => T = Impl.min as any; export const min: <T extends number = number>(array: SortedArray<T>) => T = Impl.min as any;
export const max: <T extends number = number>(array: SortedArray<T>) => T = Impl.max as any; export const max: <T extends number = number>(array: SortedArray<T>) => T = Impl.max as any;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
import { StructureSelection, StructureQuery, Structure, Queries, StructureProperties as SP, StructureElement, Unit } from '../../../mol-model/structure'; import { StructureSelection, StructureQuery, Structure, Queries, StructureProperties as SP, StructureElement, Unit } from '../../../mol-model/structure';
import { SequenceWrapper } from './util'; import { SequenceWrapper } from './util';
import { OrderedSet, Interval } from '../../../mol-data/int'; import { OrderedSet, Interval, SortedArray } from '../../../mol-data/int';
import { Loci } from '../../../mol-model/loci'; import { Loci } from '../../../mol-model/loci';
import { Sequence } from '../../../mol-model/sequence'; import { Sequence } from '../../../mol-model/sequence';
import { MissingResidues } from '../../../mol-model/structure/model/properties/common'; import { MissingResidues } from '../../../mol-model/structure/model/properties/common';
...@@ -18,46 +18,52 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> { ...@@ -18,46 +18,52 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
private readonly location: StructureElement private readonly location: StructureElement
private readonly sequence: Sequence private readonly sequence: Sequence
private readonly missing: MissingResidues private readonly missing: MissingResidues
private readonly observed: OrderedSet // sequences indices
private readonly modelNum: number private readonly modelNum: number
private readonly asymId: string private readonly asymId: string
seqId(i: number) { seqId(seqIdx: number) {
return this.sequence.offset + i + 1 return this.sequence.offset + seqIdx + 1
} }
residueLabel(i: number) { residueLabel(seqIdx: number) {
return this.sequence.sequence[i] return this.sequence.sequence[seqIdx]
} }
residueColor(i: number) { residueColor(seqIdx: number) {
return this.missing.has(this.modelNum, this.asymId, this.seqId(i)) return this.missing.has(this.modelNum, this.asymId, this.seqId(seqIdx))
? ColorNames.grey ? ColorNames.grey
: ColorNames.black : ColorNames.black
} }
eachResidue(loci: Loci, apply: (interval: Interval) => boolean) { eachResidue(loci: Loci, apply: (set: OrderedSet) => boolean) {
let changed = false let changed = false
const { structure, unit } = this.data const { structure, unit } = this.data
if (!StructureElement.isLoci(loci)) return false if (StructureElement.isLoci(loci)) {
if (!Structure.areParentsEquivalent(loci.structure, structure)) return false if (!Structure.areParentsEqual(loci.structure, structure)) return false
const { location } = this const { location } = this
for (const e of loci.elements) { for (const e of loci.elements) {
let rIprev = -1 let rIprev = -1
location.unit = e.unit location.unit = e.unit
const { index: residueIndex } = e.unit.model.atomicHierarchy.residueAtomSegments const { index: residueIndex } = e.unit.model.atomicHierarchy.residueAtomSegments
OrderedSet.forEach(e.indices, v => { OrderedSet.forEach(e.indices, v => {
location.element = e.unit.elements[v] location.element = e.unit.elements[v]
const rI = residueIndex[location.element] const rI = residueIndex[location.element]
// avoid checking for the same residue multiple times // avoid checking for the same residue multiple times
if (rI !== rIprev) { if (rI !== rIprev) {
if (SP.unit.id(location) !== unit.id) return if (SP.unit.id(location) !== unit.id) return
if (apply(getSeqIdInterval(location))) changed = true if (apply(getSeqIndices(location))) changed = true
rIprev = rI rIprev = rI
} }
}) })
}
} else if (Structure.isLoci(loci)) {
if (!Structure.areParentsEqual(loci.structure, structure)) return false
if (apply(this.observed)) changed = true
} }
return changed return changed
} }
...@@ -70,7 +76,8 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> { ...@@ -70,7 +76,8 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
constructor(data: StructureUnit) { constructor(data: StructureUnit) {
const l = StructureElement.create(data.unit, data.unit.elements[0]) const l = StructureElement.create(data.unit, data.unit.elements[0])
const sequence = data.unit.model.sequence.byEntityKey[SP.entity.key(l)].sequence const sequence = data.unit.model.sequence.byEntityKey[SP.entity.key(l)].sequence
const markerArray = new Uint8Array(sequence.sequence.length) const length = sequence.sequence.length
const markerArray = new Uint8Array(length)
super(data, markerArray, sequence.sequence.length) super(data, markerArray, sequence.sequence.length)
...@@ -80,6 +87,12 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> { ...@@ -80,6 +87,12 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
this.modelNum = data.unit.model.modelNum this.modelNum = data.unit.model.modelNum
this.asymId = SP.chain.label_asym_id(l) this.asymId = SP.chain.label_asym_id(l)
const missing: number[] = []
for (let i = 0; i < length; ++i) {
if (this.missing.has(this.modelNum, this.asymId, this.seqId(i))) missing.push(i)
}
this.observed = OrderedSet.subtract(Interval.ofBounds(0, length), SortedArray.ofSortedArray(missing))
} }
} }
...@@ -101,8 +114,7 @@ function createResidueQuery(unitId: number, label_seq_id: number) { ...@@ -101,8 +114,7 @@ function createResidueQuery(unitId: number, label_seq_id: number) {
}); });
} }
/** Zero-indexed */ function getSeqIndices(location: StructureElement): Interval {
function getSeqIdInterval(location: StructureElement): Interval {
const { unit, element } = location const { unit, element } = location
const { model } = unit const { model } = unit
switch (unit.kind) { switch (unit.kind) {
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Alexander Rose <alexander.rose@weirdbyte.de>
*/ */
import { Interval } from '../../../mol-data/int'; import { OrderedSet } from '../../../mol-data/int';
import { Loci } from '../../../mol-model/loci'; import { Loci } from '../../../mol-model/loci';
import { MarkerAction, applyMarkerAction } from '../../../mol-util/marker-action'; import { MarkerAction, applyMarkerAction } from '../../../mol-util/marker-action';
import { StructureElement } from '../../../mol-model/structure'; import { StructureElement } from '../../../mol-model/structure';
...@@ -13,16 +13,16 @@ import { Color } from '../../../mol-util/color'; ...@@ -13,16 +13,16 @@ import { Color } from '../../../mol-util/color';
export { SequenceWrapper } export { SequenceWrapper }
abstract class SequenceWrapper<D> { abstract class SequenceWrapper<D> {
abstract seqId(i: number): number abstract seqId(seqIdx: number): number
abstract residueLabel(i: number): string abstract residueLabel(seqIdx: number): string
abstract residueColor(i: number): Color abstract residueColor(seqIdx: number): Color
abstract eachResidue(loci: Loci, apply: (interval: Interval) => boolean): boolean abstract eachResidue(loci: Loci, apply: (set: OrderedSet) => boolean): boolean
abstract getLoci(seqId: number): StructureElement.Loci abstract getLoci(seqId: number): StructureElement.Loci
markResidue(loci: Loci, action: MarkerAction) { markResidue(loci: Loci, action: MarkerAction) {
return this.eachResidue(loci, (i: Interval) => { return this.eachResidue(loci, (set: OrderedSet) => {
return applyMarkerAction(this.markerArray, i, action) return applyMarkerAction(this.markerArray, set, action)
}) })
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment