diff --git a/src/mol-data/int/impl/sorted-array.ts b/src/mol-data/int/impl/sorted-array.ts index faa32048fca2e968542643436b8ca878dbfccc24..8c81e7940adde281f8950fdd78c71bbbb233cee0 100644 --- a/src/mol-data/int/impl/sorted-array.ts +++ b/src/mol-data/int/impl/sorted-array.ts @@ -65,6 +65,11 @@ export function areEqual(a: Nums, b: Nums) { return true; } +/** + * Returns 0 if `v` is smaller or equal the first element of `xs` + * Returns length of `xs` if `v` is bigger than the last element of `xs` + * Otherwise returns the first index where the value of `xs` is equal or bigger than `v` + */ export function findPredecessorIndex(xs: Nums, v: number) { const len = xs.length; if (v <= xs[0]) return 0; diff --git a/src/mol-data/int/ordered-set.ts b/src/mol-data/int/ordered-set.ts index 3b7d03d397142a93ef3286cacdf5c1b5948eb587..d8e7f705d5c0d7a16991f24c104970a6b1fbd80d 100644 --- a/src/mol-data/int/ordered-set.ts +++ b/src/mol-data/int/ordered-set.ts @@ -41,6 +41,11 @@ namespace OrderedSet { /** 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; + /** + * Returns 0 if `x` is smaller or equal the first element of `set` + * Returns length of `set` if `x` is bigger than the last element of `set` + * Otherwise returns the first index where the value of `set` is equal or bigger than `x` + */ export const findPredecessorIndex: <T extends number = number>(set: OrderedSet<T>, x: number) => number = Base.findPredecessorIndex as any; export const findPredecessorIndexInInterval: <T extends number = number>(set: OrderedSet<T>, x: T, range: Interval) => number = Base.findPredecessorIndexInInterval as any; export const findRange: <T extends number = number>(set: OrderedSet<T>, min: T, max: T) => Interval = Base.findRange as any; diff --git a/src/mol-data/int/sorted-ranges.ts b/src/mol-data/int/sorted-ranges.ts index 18b7c6b6470f5c3945e35894b17861c986e353eb..754946a5b9221a83bb2f9b5e6dff6d0c49c37a14 100644 --- a/src/mol-data/int/sorted-ranges.ts +++ b/src/mol-data/int/sorted-ranges.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -23,6 +23,46 @@ namespace SortedRanges { } return size } + export function count<T extends number = number>(ranges: SortedRanges<T>) { return ranges.length / 2 } + + export function startAt<T extends number = number>(ranges: SortedRanges<T>, index: number) { + return ranges[index * 2] + } + export function endAt<T extends number = number>(ranges: SortedRanges<T>, index: number) { + return ranges[index * 2 + 1] + 1 + } + + export function minAt<T extends number = number>(ranges: SortedRanges<T>, index: number) { + return ranges[index * 2] + } + export function maxAt<T extends number = number>(ranges: SortedRanges<T>, index: number) { + return ranges[index * 2 + 1] + } + + /** Returns if a value of `set` is included in `ranges` */ + export function has<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>) { + return firstIntersectionIndex(ranges, set) !== -1 + } + + /** Returns if a value of `set` is included in `ranges` from given index */ + export function hasFrom<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>, from: number) { + return firstIntersectionIndexFrom(ranges, set, from) !== -1 + } + + export function firstIntersectionIndex<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>): number { + return firstIntersectionIndexFrom(ranges, set, 0) + } + + export function firstIntersectionIndexFrom<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>, from: number): number { + if (minAt(ranges, from) > OrderedSet.max(set) || max(ranges) < OrderedSet.min(set)) return -1 + + for (let i = from, il = count(ranges); i < il; ++i) { + const interval = Interval.ofRange(minAt(ranges, i), maxAt(ranges, i)) + if (OrderedSet.areIntersecting(interval, set)) return i + } + + return -1 + } export function transientSegments<T extends number = number, I extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>) { return new Iterator<T, I>(ranges, set) @@ -32,52 +72,27 @@ namespace SortedRanges { private value: Segmentation.Segment<I> = { index: 0 as I, start: 0 as T, end: 0 as T } private curIndex = 0 - private maxIndex = 0 - private interval: Interval<T> - private curMin: T = 0 as T hasNext: boolean = false; - private updateInterval() { - this.interval = Interval.ofRange(this.ranges[this.curIndex], this.ranges[this.curIndex + 1]) - } - private updateValue() { - this.value.index = this.curIndex / 2 as I - this.value.start = OrderedSet.findPredecessorIndex(this.set, this.ranges[this.curIndex]) - this.value.end = OrderedSet.findPredecessorIndex(this.set, this.ranges[this.curIndex + 1]) + this.value.index = this.curIndex as I + this.value.start = OrderedSet.findPredecessorIndex(this.set, startAt(this.ranges, this.curIndex)) + this.value.end = OrderedSet.findPredecessorIndex(this.set, endAt(this.ranges, this.curIndex)) } move() { if (this.hasNext) { this.updateValue() - while (this.curIndex <= this.maxIndex) { - this.curIndex += 2 - this.curMin = Interval.end(this.interval) - this.updateInterval() - if (Interval.min(this.interval) >= this.curMin && OrderedSet.areIntersecting(this.interval, this.set)) break - } - this.hasNext = this.curIndex <= this.maxIndex + this.curIndex = firstIntersectionIndexFrom(this.ranges, this.set, this.curIndex + 1) + this.hasNext = this.curIndex !== -1 } return this.value; } - private getRangeIndex(value: number) { - const index = SortedArray.findPredecessorIndex(this.ranges, value) - return (index % 2 === 1) ? index - 1 : index - } - constructor(private ranges: SortedRanges<T>, private set: OrderedSet<T>) { - const min = OrderedSet.min(set) - const max = OrderedSet.max(set) - const b = ranges.length > 0 && ranges[0] <= max && ranges[ranges.length - 1] >= min - if (b) { - this.curIndex = this.getRangeIndex(min) - this.maxIndex = Math.min(ranges.length - 2, this.getRangeIndex(max) + 2) - this.curMin = ranges[this.curIndex] - this.updateInterval() - } - this.hasNext = b && this.curIndex <= this.maxIndex + this.curIndex = firstIntersectionIndex(ranges, set) + this.hasNext = this.curIndex !== -1 } } } diff --git a/src/mol-model/structure/structure/element.ts b/src/mol-model/structure/structure/element.ts index 80096d9695cff7950a898119c9da95470acf5824..39ef7d4e0b2250a004967de3ad0ec418f41d1022 100644 --- a/src/mol-model/structure/structure/element.ts +++ b/src/mol-model/structure/structure/element.ts @@ -166,6 +166,7 @@ namespace StructureElement { return Loci(structure, elements); } + /** Create union of `xs` and `ys` */ export function union(xs: Loci, ys: Loci): Loci { if (xs.elements.length > ys.elements.length) return union(ys, xs); if (xs.elements.length === 0) return ys; @@ -191,6 +192,7 @@ namespace StructureElement { return Loci(xs.structure, elements); } + /** Subtract `ys` from `xs` */ export function subtract(xs: Loci, ys: Loci): Loci { const map = new Map<number, OrderedSet<UnitIndex>>(); for (const e of ys.elements) map.set(e.unit.id, e.indices);