Skip to content
Snippets Groups Projects
Commit 53c8ba54 authored by David Sehnal's avatar David Sehnal
Browse files

wip selection model

parent 0e0529d3
No related branches found
No related tags found
No related merge requests found
...@@ -25,8 +25,6 @@ export namespace ModelInfo { ...@@ -25,8 +25,6 @@ export namespace ModelInfo {
const json = JSON.parse(src); const json = JSON.parse(src);
const data = json && json[id]; const data = json && json[id];
console.log(data);
const assemblies = data[0] && data[0].assemblies; const assemblies = data[0] && data[0].assemblies;
if (!assemblies || !assemblies.length) return void 0; if (!assemblies || !assemblies.length) return void 0;
......
...@@ -47,7 +47,7 @@ export function intersect(queries: ArrayLike<StructureQuery>): StructureQuery { ...@@ -47,7 +47,7 @@ export function intersect(queries: ArrayLike<StructureQuery>): StructureQuery {
} }
ctx.throwIfTimedOut(); ctx.throwIfTimedOut();
const pivotSet = HashSet<Structure>(s => s.hashCode, Structure.areEqual); const pivotSet = HashSet<Structure>(s => s.hashCode, Structure.areUnitAndIndicesEqual);
StructureSelection.forEach(selections[pivotIndex], s => pivotSet.add(s)); StructureSelection.forEach(selections[pivotIndex], s => pivotSet.add(s));
const ret = StructureSelection.UniqueBuilder(ctx.inputStructure); const ret = StructureSelection.UniqueBuilder(ctx.inputStructure);
......
...@@ -84,7 +84,7 @@ namespace StructureSelection { ...@@ -84,7 +84,7 @@ namespace StructureSelection {
class HashBuilderImpl implements Builder { class HashBuilderImpl implements Builder {
private structures: Structure[] = []; private structures: Structure[] = [];
private allSingletons = true; private allSingletons = true;
private uniqueSets = HashSet(Structure.hashCode, Structure.areEqual); private uniqueSets = HashSet(Structure.hashCode, Structure.areUnitAndIndicesEqual);
add(structure: Structure) { add(structure: Structure) {
const atomCount = structure.elementCount; const atomCount = structure.elementCount;
......
...@@ -12,7 +12,7 @@ import { StructureSubsetBuilder } from '../../structure/util/subset-builder'; ...@@ -12,7 +12,7 @@ import { StructureSubsetBuilder } from '../../structure/util/subset-builder';
import { ElementIndex } from '../../model'; import { ElementIndex } from '../../model';
export class UniqueStructuresBuilder { export class UniqueStructuresBuilder {
private set = HashSet(Structure.hashCode, Structure.areEqual); private set = HashSet(Structure.hashCode, Structure.areUnitAndIndicesEqual);
private structures: Structure[] = []; private structures: Structure[] = [];
private allSingletons = true; private allSingletons = true;
......
...@@ -117,6 +117,13 @@ namespace StructureElement { ...@@ -117,6 +117,13 @@ namespace StructureElement {
}))); })));
} }
export function remap(loci: Loci, structure: Structure): Loci {
return Loci(structure, loci.elements.map(e => ({
unit: structure.unitMap.get(e.unit.id)!,
indices: e.indices
})));
}
export function union(xs: Loci, ys: Loci): Loci { export function union(xs: Loci, ys: Loci): Loci {
if (xs.structure !== ys.structure) throw new Error(`Can't union Loci of different structures.`); if (xs.structure !== ys.structure) throw new Error(`Can't union Loci of different structures.`);
if (xs.elements.length > ys.elements.length) return union(ys, xs); if (xs.elements.length > ys.elements.length) return union(ys, xs);
......
...@@ -424,7 +424,7 @@ namespace Structure { ...@@ -424,7 +424,7 @@ namespace Structure {
return hashString(s.units.map(u => Unit.conformationId(u)).join('|')) return hashString(s.units.map(u => Unit.conformationId(u)).join('|'))
} }
export function areEqual(a: Structure, b: Structure) { export function areUnitAndIndicesEqual(a: Structure, b: Structure) {
if (a.elementCount !== b.elementCount) return false; if (a.elementCount !== b.elementCount) return false;
const len = a.units.length; const len = a.units.length;
if (len !== b.units.length) return false; if (len !== b.units.length) return false;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
import { PluginContext } from '../context'; import { PluginContext } from '../context';
import { PickingId } from 'mol-geo/geometry/picking'; import { PickingId } from 'mol-geo/geometry/picking';
import { EmptyLoci, Loci } from 'mol-model/loci'; import { EmptyLoci } from 'mol-model/loci';
import { Representation } from 'mol-repr/representation'; import { Representation } from 'mol-repr/representation';
import { ModifiersKeys, ButtonsType } from 'mol-util/input/input-observer'; import { ModifiersKeys, ButtonsType } from 'mol-util/input/input-observer';
...@@ -49,7 +49,7 @@ export class Canvas3dIdentifyHelper { ...@@ -49,7 +49,7 @@ export class Canvas3dIdentifyHelper {
} }
const loci = this.ctx.canvas3d.getLoci(this.id); const loci = this.ctx.canvas3d.getLoci(this.id);
if (loci.repr !== this.prevLoci.repr || !Loci.areEqual(loci.loci, this.prevLoci.loci)) { if (!Representation.Loci.areEqual(this.prevLoci, loci)) {
this.ctx.events.canvas3d.highlight.next({ loci, modifiers: this.modifiers }); this.ctx.events.canvas3d.highlight.next({ loci, modifiers: this.modifiers });
this.prevLoci = loci; this.prevLoci = loci;
} }
......
...@@ -8,27 +8,25 @@ import { StructureElement, Structure } from 'mol-model/structure'; ...@@ -8,27 +8,25 @@ import { StructureElement, Structure } from 'mol-model/structure';
import { PluginContext } from '../../context'; import { PluginContext } from '../../context';
import { Loci, EmptyLoci } from 'mol-model/loci'; import { Loci, EmptyLoci } from 'mol-model/loci';
import { PluginStateObject } from 'mol-plugin/state/objects'; import { PluginStateObject } from 'mol-plugin/state/objects';
import { State } from 'mol-state'; import { State, StateSelection, StateObject } from 'mol-state';
import { mapObjectMap } from 'mol-util/object';
export { StructureElementSelectionManager } export { StructureLociManager }
class StructureElementSelectionManager { class StructureLociManager {
private entries = new Map<string, Entry>(); private entries = new Map<string, SelectionEntry>();
// maps structure to a parent StateObjectCell // maps structure to a parent StateObjectCell
private structures = { private mapping = {
root: new Map<Structure, string>(), root: new Map<Structure, string>(),
tracked: new Map<string, Structure>() tracked: new Map<string, Structure>()
} };
private getEntry(s: Structure) { private getEntry(s: Structure) {
if (!this.structures.root.has(s)) return; if (!this.mapping.root.has(s)) return;
const key = this.structures.root.get(s)!; const key = this.mapping.root.get(s)!;
if (!this.entries.has(key)) { if (!this.entries.has(key)) {
const entry: Entry = { const entry = SelectionEntry(s);
selection: StructureElement.Loci(s, []),
highlight: StructureElement.Loci(s, []),
};
this.entries.set(key, entry); this.entries.set(key, entry);
return entry; return entry;
} }
...@@ -39,22 +37,25 @@ class StructureElementSelectionManager { ...@@ -39,22 +37,25 @@ class StructureElementSelectionManager {
add(loci: StructureElement.Loci | Structure.Loci, type: 'selection' | 'highlight'): Loci { add(loci: StructureElement.Loci | Structure.Loci, type: 'selection' | 'highlight'): Loci {
const entry = this.getEntry(loci.structure); const entry = this.getEntry(loci.structure);
if (!entry) return EmptyLoci; if (!entry) return EmptyLoci;
entry[type] = Structure.isLoci(loci) ? StructureElement.Loci.all(loci.structure) : StructureElement.Loci.union(entry[type], loci); const xs = entry.elements;
return entry[type]; xs[type] = Structure.isLoci(loci) ? StructureElement.Loci.all(loci.structure) : StructureElement.Loci.union(xs[type], loci);
return xs[type];
} }
remove(loci: StructureElement.Loci | Structure.Loci, type: 'selection' | 'highlight'): Loci { remove(loci: StructureElement.Loci | Structure.Loci, type: 'selection' | 'highlight'): Loci {
const entry = this.getEntry(loci.structure); const entry = this.getEntry(loci.structure);
if (!entry) return EmptyLoci; if (!entry) return EmptyLoci;
entry[type] = Structure.isLoci(loci) ? StructureElement.Loci(loci.structure, []) : StructureElement.Loci.subtract(entry[type], loci); const xs = entry.elements;
return entry[type].elements.length === 0 ? EmptyLoci : entry[type]; xs[type] = Structure.isLoci(loci) ? StructureElement.Loci(loci.structure, []) : StructureElement.Loci.subtract(xs[type], loci);
return xs[type].elements.length === 0 ? EmptyLoci : xs[type];
} }
set(loci: StructureElement.Loci | Structure.Loci, type: 'selection' | 'highlight'): Loci { set(loci: StructureElement.Loci | Structure.Loci, type: 'selection' | 'highlight'): Loci {
const entry = this.getEntry(loci.structure); const entry = this.getEntry(loci.structure);
if (!entry) return EmptyLoci; if (!entry) return EmptyLoci;
entry[type] = Structure.isLoci(loci) ? StructureElement.Loci.all(loci.structure) : loci; const xs = entry.elements;
return entry[type].elements.length === 0 ? EmptyLoci : entry[type]; xs[type] = Structure.isLoci(loci) ? StructureElement.Loci.all(loci.structure) : loci;
return xs[type].elements.length === 0 ? EmptyLoci : xs[type];
} }
private prevHighlight: StructureElement.Loci | undefined = void 0; private prevHighlight: StructureElement.Loci | undefined = void 0;
...@@ -74,42 +75,73 @@ class StructureElementSelectionManager { ...@@ -74,42 +75,73 @@ class StructureElementSelectionManager {
return ret || EmptyLoci; return ret || EmptyLoci;
} }
private trackCell(state: State, ref: string) { private addMapping(state: State, ref: string, obj: StateObject) {
const cell = state.cells.get(ref); if (!PluginStateObject.Molecule.Structure.is(obj)) return;
if (!cell || !cell.obj || !PluginStateObject.Molecule.Structure.is(cell.obj)) return; const parent = state.select(StateSelection.Generators.byRef(ref).rootOfType([PluginStateObject.Molecule.Structure]))[0];
const parent = state.selectQ(q => q.byRef(cell.transform.ref).rootOfType([PluginStateObject.Molecule.Structure]))[0]; this.mapping.tracked.set(ref, obj.data);
this.structures.tracked.set(cell.transform.ref, cell.obj.data);
if (!parent) { if (!parent) {
this.structures.root.set(cell.obj.data, cell.transform.ref); this.mapping.root.set(obj.data, ref);
} else { } else {
this.structures.root.set(cell.obj.data, parent.transform.ref); this.mapping.root.set(obj.data, parent.transform.ref);
} }
} }
private untrackCell(state: State, ref: string) { private removeMapping(ref: string) {
if (!this.structures.tracked.has(ref)) return; if (!this.mapping.tracked.has(ref)) return;
const s = this.structures.tracked.get(ref)!; const s = this.mapping.tracked.get(ref)!;
this.structures.tracked.delete(ref); this.mapping.tracked.delete(ref);
this.structures.root.delete(s); const root = this.mapping.root.get(s);
this.mapping.root.delete(s);
if (root === ref) this.entries.delete(ref);
}
private updateMapping(state: State, ref: string, oldObj: StateObject | undefined, obj: StateObject) {
if (!PluginStateObject.Molecule.Structure.is(obj)) return;
if (this.entries.has(ref)) {
if (!PluginStateObject.Molecule.Structure.is(oldObj) || oldObj === obj || oldObj.data === obj.data) return;
// remap the old selection to be related to the new object if possible.
if (Structure.areUnitAndIndicesEqual(oldObj.data, obj.data)) {
this.entries.set(ref, remapSelectionEntry(this.entries.get(ref)!, obj.data));
return;
}
// clear the selection
this.entries.set(ref, SelectionEntry(obj.data));
} else {
this.removeMapping(ref);
this.addMapping(state, ref, obj);
}
} }
constructor(plugin: PluginContext) { constructor(plugin: PluginContext) {
plugin.state.dataState.events.object.created.subscribe(e => { plugin.state.dataState.events.object.created.subscribe(e => {
this.trackCell(e.state, e.ref); this.addMapping(e.state, e.ref, e.obj);
}); });
plugin.state.dataState.events.object.removed.subscribe(e => { plugin.state.dataState.events.object.removed.subscribe(e => {
this.untrackCell(e.state, e.ref); this.removeMapping(e.ref);
}); });
plugin.state.dataState.events.object.updated.subscribe(e => { plugin.state.dataState.events.object.updated.subscribe(e => {
this.untrackCell(e.state, e.ref); this.updateMapping(e.state, e.ref, e.oldObj, e.obj);
this.trackCell(e.state, e.ref);
}); });
} }
} }
interface Entry { interface SelectionEntry {
selection: StructureElement.Loci, elements: { [category: string]: StructureElement.Loci }
highlight: StructureElement.Loci }
function SelectionEntry(s: Structure): SelectionEntry {
return {
elements: { }
};
}
function remapSelectionEntry(e: SelectionEntry, s: Structure): SelectionEntry {
return {
elements: mapObjectMap(e.elements, (l: StructureElement.Loci) => StructureElement.Loci.remap(l, s))
};
} }
\ No newline at end of file
...@@ -83,3 +83,11 @@ export function deepClone<T>(source: T): T { ...@@ -83,3 +83,11 @@ export function deepClone<T>(source: T): T {
throw new Error(`Can't clone, type "${typeof source}" unsupported`); throw new Error(`Can't clone, type "${typeof source}" unsupported`);
} }
export function mapObjectMap<O extends { [k: string]: T }, T, S>(o: O, f: (v: T) => S): { [k: string]: S } {
const ret: any = { };
for (const k of Object.keys(o)) {
ret[k] = f((o as any)[k]);
}
return ret;
}
\ 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