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

added StructureElement.Query for Loci serialization

parent a6319bfb
No related branches found
No related tags found
No related merge requests found
...@@ -73,7 +73,7 @@ export function sortedCantorPairing(a: number, b: number) { ...@@ -73,7 +73,7 @@ export function sortedCantorPairing(a: number, b: number) {
/** /**
* 32 bit FNV-1a hash, see http://isthe.com/chongo/tech/comp/fnv/ * 32 bit FNV-1a hash, see http://isthe.com/chongo/tech/comp/fnv/
*/ */
export function hashFnv32a(array: number[]) { export function hashFnv32a(array: ArrayLike<number>) {
let hval = 0x811c9dc5; let hval = 0x811c9dc5;
for (let i = 0, il = array.length; i < il; ++i) { for (let i = 0, il = array.length; i < il; ++i) {
hval ^= array[i]; hval ^= array[i];
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
*/ */
import { UniqueArray } from '../../../mol-data/generic'; import { UniqueArray } from '../../../mol-data/generic';
import { OrderedSet, SortedArray } from '../../../mol-data/int'; import { OrderedSet, SortedArray, Interval } from '../../../mol-data/int';
import { BoundaryHelper } from '../../../mol-math/geometry/boundary-helper'; import { BoundaryHelper } from '../../../mol-math/geometry/boundary-helper';
import { Vec3 } from '../../../mol-math/linear-algebra'; import { Vec3 } from '../../../mol-math/linear-algebra';
import { MolScriptBuilder as MS } from '../../../mol-script/language/builder'; import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
...@@ -16,8 +16,9 @@ import Structure from './structure'; ...@@ -16,8 +16,9 @@ import Structure from './structure';
import Unit from './unit'; import Unit from './unit';
import { Boundary } from './util/boundary'; import { Boundary } from './util/boundary';
import { StructureProperties } from '../structure'; import { StructureProperties } from '../structure';
import { sortArray, hashFnv32a, hash2 } from '../../../mol-data/util'; import { sortArray, hashFnv32a, hash2, hash3 } from '../../../mol-data/util';
import Expression from '../../../mol-script/language/expression'; import Expression from '../../../mol-script/language/expression';
import SortedRanges from '../../../mol-data/int/sorted-ranges';
interface StructureElement<U = Unit> { interface StructureElement<U = Unit> {
readonly kind: 'element-location', readonly kind: 'element-location',
...@@ -115,7 +116,7 @@ namespace StructureElement { ...@@ -115,7 +116,7 @@ namespace StructureElement {
export function entityIndex(l: StructureElement) { export function entityIndex(l: StructureElement) {
return StructureProperties.entity.key(l) return StructureProperties.entity.key(l)
} }
export namespace Loci { export namespace Loci {
export function size(loci: Loci) { export function size(loci: Loci) {
...@@ -461,6 +462,177 @@ namespace StructureElement { ...@@ -461,6 +462,177 @@ namespace StructureElement {
} }
} }
interface QueryElement {
/** Array of `Unit.id`s that share the same `Unit.invariantId` */
units: SortedArray<number>,
set: SortedArray<UnitIndex>
ranges: SortedRanges<UnitIndex>
}
export interface Query {
readonly elements: ReadonlyArray<Readonly<QueryElement>>
}
export namespace Query {
export const Empty: Query = { elements: [] }
export function fromLoci(loci: StructureElement.Loci): Query {
const _elements: {
unit: Unit
set: SortedArray<UnitIndex>
ranges: SortedRanges<UnitIndex>
}[] = []
for (const e of loci.elements) {
const { unit, indices } = e
const ranges: UnitIndex[] = [];
const set: UnitIndex[] = [];
if (OrderedSet.isInterval(indices)) {
ranges[ranges.length] = Interval.min(indices)
ranges[ranges.length] = Interval.max(indices)
} else {
let i = 0, len = indices.length;
while (i < len) {
const start = i;
i++;
while (i < len && indices[i - 1] + 1 === indices[i]) i++;
const end = i;
// TODO: is this a good value?
if (end - start > 12) {
ranges[ranges.length] = indices[start];
ranges[ranges.length] = indices[end - 1];
} else {
for (let j = start; j < end; j++) {
set[set.length] = indices[j];
}
}
}
}
_elements.push({
unit,
set: SortedArray.ofSortedArray(set),
ranges: SortedRanges.ofSortedRanges(ranges)
})
}
const elementGroups = new Map<number, {
units: number[]
set: SortedArray<UnitIndex>
ranges: SortedRanges<UnitIndex>
}>();
for (let i = 0, il = _elements.length; i < il; ++i) {
const e = _elements[i]
const key = hash3(hashFnv32a(e.ranges), hashFnv32a(e.set), e.unit.invariantId)
if (elementGroups.has(key)) {
elementGroups.get(key)!.units.push(e.unit.id)
} else {
elementGroups.set(key, {
units: [e.unit.id],
set: e.set,
ranges: e.ranges
})
}
}
const elements: QueryElement[] = []
elementGroups.forEach(e => {
elements.push({
units: SortedArray.ofUnsortedArray(e.units),
set: e.set,
ranges: e.ranges
})
})
return { elements }
}
function getUnitsFromIds(unitIds: ArrayLike<number>, structure: Structure) {
const units: Unit[] = []
for (let i = 0, il = unitIds.length; i < il; ++i) {
const unitId = unitIds[i]
if (structure.unitMap.has(unitId)) units.push(structure.unitMap.get(unitId))
}
return units
}
export function toLoci(query: Query, parent: Structure): Loci {
const elements: Loci['elements'][0][] = []
for (const e of query.elements) {
const units = getUnitsFromIds(e.units, parent)
if (units.length === 0) continue
let indices: OrderedSet<UnitIndex>
if (e.ranges.length === 0) {
indices = e.set
} else if (e.set.length === 0) {
if (e.ranges.length === 2) {
indices = Interval.ofRange(e.ranges[0], e.ranges[1])
} else {
const _indices = new Int32Array(SortedRanges.size(e.ranges))
SortedRanges.forEach(e.ranges, (v, i) => _indices[i] = v)
indices = SortedArray.ofSortedArray(_indices)
}
} else {
const rangesSize = SortedRanges.size(e.ranges)
const _indices = new Int32Array(e.set.length + rangesSize)
SortedRanges.forEach(e.ranges, (v, i) => _indices[i] = v)
_indices.set(e.set, rangesSize)
indices = SortedArray.ofUnsortedArray(_indices) // requires sort
}
for (const unit of units) {
elements.push({ unit, indices })
}
}
return Loci(parent, elements)
}
export function toStructure(query: Query, parent: Structure): Structure {
const units: Unit[] = []
for (const e of query.elements) {
const _units = getUnitsFromIds(e.units, parent)
if (_units.length === 0) continue
const unit = _units[0] // the elements are grouped by unit.invariantId
const rangesSize = SortedRanges.size(e.ranges)
const _indices = new Int32Array(e.set.length + rangesSize)
let indices: SortedArray<ElementIndex>
if (e.ranges.length === 0) {
for (let i = 0, il = e.set.length; i < il; ++i) {
_indices[i] = unit.elements[e.set[i]]
}
indices = SortedArray.ofSortedArray(_indices)
} else if (e.set.length === 0) {
SortedRanges.forEach(e.ranges, (v, i) => _indices[i] = unit.elements[v])
indices = SortedArray.ofSortedArray(_indices)
} else {
const rangesSize = SortedRanges.size(e.ranges)
SortedRanges.forEach(e.ranges, (v, i) => _indices[i] = unit.elements[v])
SortedRanges.forEach(e.set, (v, i) => _indices[i + rangesSize] = unit.elements[v])
indices = SortedArray.ofUnsortedArray(_indices) // requires sort
}
for (const unit of _units) {
units.push(unit.getChild(indices))
}
}
return Structure.create(units, parent)
}
export function areEqual(a: Query, b: Query) {
if (a.elements.length !== b.elements.length) return false
for (let i = 0, il = a.elements.length; i < il; ++i) {
const elementA = a.elements[i]
const elementB = b.elements[i]
if (!SortedArray.areEqual(elementA.units, elementB.units)) return false
if (!SortedArray.areEqual(elementA.set, elementB.set)) return false
if (!SortedRanges.areEqual(elementA.ranges, elementB.ranges)) return false
}
return true
}
}
} }
export default StructureElement export default StructureElement
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment