/** * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> */ import { HashSet } from 'mol-data/generic' import { Structure } from '../structure' import { structureUnion } from './utils/structure'; // A selection is a pair of a Structure and a sequence of unique AtomSets type Selection = Selection.Singletons | Selection.Sequence namespace Selection { // If each element of the selection is a singleton, we can use a more efficient representation. export interface Singletons { readonly kind: 'singletons', readonly source: Structure, readonly structure: Structure } export interface Sequence { readonly kind: 'sequence', readonly source: Structure, readonly structures: Structure[] } export function Singletons(source: Structure, structure: Structure): Singletons { return { kind: 'singletons', source, structure } } export function Sequence(source: Structure, structures: Structure[]): Sequence { return { kind: 'sequence', source, structures } } export function Empty(source: Structure): Selection { return Singletons(source, Structure.Empty); }; export function isSingleton(s: Selection): s is Singletons { return s.kind === 'singletons'; } export function isEmpty(s: Selection) { return isSingleton(s) ? s.structure.units.length === 0 : s.structures.length === 0; } export function structureCount(sel: Selection) { if (isSingleton(sel)) return sel.structure.elementCount; return sel.structures.length; } export function unionStructure(sel: Selection): Structure { if (isEmpty(sel)) return Structure.Empty; if (isSingleton(sel)) return sel.structure; return structureUnion(sel.source, sel.structures); } export interface Builder { add(structure: Structure): void, getSelection(): Selection } function getSelection(source: Structure, structures: Structure[], allSingletons: boolean) { const len = structures.length; if (len === 0) return Empty(source); if (allSingletons) return Singletons(source, structureUnion(source, structures)); return Sequence(source, structures); } class LinearBuilderImpl implements Builder { private structures: Structure[] = []; private allSingletons = true; add(structure: Structure) { const elementCount = structure.elementCount; if (elementCount === 0) return; this.structures[this.structures.length] = structure; if (elementCount !== 1) this.allSingletons = false; } getSelection() { return getSelection(this.source, this.structures, this.allSingletons); } constructor(private source: Structure) { } } class HashBuilderImpl implements Builder { private structures: Structure[] = []; private allSingletons = true; private uniqueSets = HashSet(Structure.hashCode, Structure.areEqual); add(structure: Structure) { const atomCount = structure.elementCount; if (atomCount === 0 || !this.uniqueSets.add(structure)) return; this.structures[this.structures.length] = structure; if (atomCount !== 1) this.allSingletons = false; } getSelection() { return getSelection(this.structure, this.structures, this.allSingletons); } constructor(private structure: Structure) { } } export function LinearBuilder(structure: Structure): Builder { return new LinearBuilderImpl(structure); } export function UniqueBuilder(structure: Structure): Builder { return new HashBuilderImpl(structure); } // TODO: spatial lookup } export default Selection