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

wip: volume streaming

parent e57ebb54
No related branches found
No related tags found
No related merge requests found
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import CIF from 'mol-io/reader/cif';
import { Box3D } from 'mol-math/geometry';
import { Vec3 } from 'mol-math/linear-algebra';
import { volumeFromDensityServerData } from 'mol-model-formats/volume/density-server';
import { VolumeData } from 'mol-model/volume';
import { PluginContext } from 'mol-plugin/context';
import { PluginStateObject } from 'mol-plugin/state/objects';
import { IsoValueParam } from 'mol-repr/volume/isosurface';
import { Color } from 'mol-util/color';
import { ColorNames } from 'mol-util/color/tables';
import { LRUCache } from 'mol-util/lru-cache';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import { PluginBehavior } from '../behavior';
export namespace VolumeStreamingBehavior {
function channelParam(label: string) { return PD.Group({ color: PD.Color(Color(ColorNames.teal)), isoValue: IsoValueParam }, { label }); }
export const Params = {
id: PD.Text(''),
levels: PD.MappedStatic('em', {
'em': channelParam('EM'),
'x-ray': PD.Group({
'2fo-fc': channelParam('2Fo-Fc'),
'fo-fc(+ve)': channelParam('Fo-Fc(+ve)'),
'fo-fc(-ve)': channelParam('Fo-Fc(-ve)'),
})
}),
box: PD.MappedStatic('static-box', {
'static-box': PD.Group({
bottomLeft: PD.Vec3(Vec3.create(0, 0, 0)),
topRight: PD.Vec3(Vec3.create(1, 1, 1))
}, { description: 'Static box defined by cartesian coords.' }),
// 'around-selection': PD.Group({ radius: PD.Numeric(5, { min: 0, max: 10 }) }),
}),
detailLevel: PD.Numeric(3, { min: 0, max: 7 }),
serverUrl: PD.Text('https://webchem.ncbr.muni.cz/DensityServer'),
}
export type Params = PD.Values<typeof Params>
export type ChannelData = { [name in 'EM' | '2FO-FC' | 'FO-FC']?: VolumeData }
export type LevelType = 'em' | '2fo-fc' | 'fo-fc(+ve)' | 'fo-fc(-ve)'
export class Behavior implements PluginBehavior<Params> {
private cache = LRUCache.create<ChannelData>(25);
currentData: ChannelData = { }
private async queryData(box?: Box3D) {
let url = `${this.params.serverUrl}/${this.params.levels.name}/${this.params.id}`
if (box) {
const { min: a, max: b } = box;
url += `/box`
+ `/${a.map(v => Math.round(1000 * v) / 1000).join(',')}`
+ `/${b.map(v => Math.round(1000 * v) / 1000).join(',')}`;
} else {
url += `/cell`;
}
url += `?detail=${this.params.detailLevel}`;
let data = LRUCache.get(this.cache, url);
if (data) {
return data;
}
const cif = await this.ctx.runTask(this.ctx.fetch(url, 'binary'));
data = await this.parseCif(cif as Uint8Array);
if (!data) {
return;
}
LRUCache.set(this.cache, url, data);
return data;
}
private async parseCif(data: Uint8Array): Promise<ChannelData | undefined> {
const parsed = await this.ctx.runTask(CIF.parseBinary(data));
if (parsed.isError) {
this.ctx.log.error('VolumeStreaming, parsing CIF: ' + parsed.toString());
return;
}
if (parsed.result.blocks.length < 2) {
this.ctx.log.error('VolumeStreaming: Invalid data.');
return;
}
const ret: ChannelData = { };
for (let i = 1; i < parsed.result.blocks.length; i++) {
const block = parsed.result.blocks[i];
const densityServerCif = CIF.schema.densityServer(block);
const volume = this.ctx.runTask(await volumeFromDensityServerData(densityServerCif));
(ret as any)[block.header as any] = volume;
}
return ret;
}
register(ref: string): void {
this.update(this.params);
}
async update(params: Params): Promise<boolean> {
this.params = params;
const box: Box3D = Box3D.create(params.box.params.bottomLeft, params.box.params.topRight);
const data = await this.queryData(box);
this.currentData = data || { };
return true;
}
unregister(): void {
}
constructor(public ctx: PluginContext, public params: Params) {
}
}
export class Obj extends PluginStateObject.CreateBehavior<Behavior>({ name: 'Volume Streaming' }) { }
}
\ No newline at end of file
......@@ -81,7 +81,7 @@ const Ccp4Provider: DataFormatProvider<any> = {
getDefaultBuilder: (ctx: PluginContext, data: StateBuilder.To<PluginStateObject.Data.Binary>, state: State) => {
return Task.create('CCP4/MRC/BRIX default builder', async taskCtx => {
const tree = data.apply(StateTransforms.Data.ParseCcp4)
.apply(StateTransforms.Model.VolumeFromCcp4)
.apply(StateTransforms.Volume.VolumeFromCcp4)
.apply(StateTransforms.Representation.VolumeRepresentation3D)
await state.updateTree(tree).runInContext(taskCtx)
})
......@@ -98,7 +98,7 @@ const Dsn6Provider: DataFormatProvider<any> = {
getDefaultBuilder: (ctx: PluginContext, data: StateBuilder.To<PluginStateObject.Data.Binary>, state: State) => {
return Task.create('DSN6/BRIX default builder', async taskCtx => {
const tree = data.apply(StateTransforms.Data.ParseDsn6)
.apply(StateTransforms.Model.VolumeFromDsn6)
.apply(StateTransforms.Volume.VolumeFromDsn6)
.apply(StateTransforms.Representation.VolumeRepresentation3D)
await state.updateTree(tree).runInContext(taskCtx)
})
......@@ -121,14 +121,14 @@ const DscifProvider: DataFormatProvider<any> = {
let tree: StateBuilder.To<any>
if (blocks.length === 1) {
tree = b
.apply(StateTransforms.Model.VolumeFromDensityServerCif, { blockHeader: blocks[0].header })
.apply(StateTransforms.Volume.VolumeFromDensityServerCif, { blockHeader: blocks[0].header })
.apply(StateTransforms.Representation.VolumeRepresentation3D, VolumeRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'isosurface', { isoValue: VolumeIsoValue.relative(1.5), alpha: 0.3 }, 'uniform', { value: ColorNames.teal }))
} else if (blocks.length === 2) {
tree = b
.apply(StateTransforms.Model.VolumeFromDensityServerCif, { blockHeader: blocks[0].header })
.apply(StateTransforms.Volume.VolumeFromDensityServerCif, { blockHeader: blocks[0].header })
.apply(StateTransforms.Representation.VolumeRepresentation3D, VolumeRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'isosurface', { isoValue: VolumeIsoValue.relative(1.5), alpha: 0.3 }, 'uniform', { value: ColorNames.blue }))
const vol = tree.to(cifBuilder.ref)
.apply(StateTransforms.Model.VolumeFromDensityServerCif, { blockHeader: blocks[1].header })
.apply(StateTransforms.Volume.VolumeFromDensityServerCif, { blockHeader: blocks[1].header })
const posParams = VolumeRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'isosurface', { isoValue: VolumeIsoValue.relative(3), alpha: 0.3 }, 'uniform', { value: ColorNames.green })
tree = vol.apply(StateTransforms.Representation.VolumeRepresentation3D, posParams)
const negParams = VolumeRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'isosurface', { isoValue: VolumeIsoValue.relative(-3), alpha: 0.3 }, 'uniform', { value: ColorNames.red })
......
......@@ -6,10 +6,12 @@
import * as Data from './transforms/data'
import * as Model from './transforms/model'
import * as Volume from './transforms/volume'
import * as Representation from './transforms/representation'
export const StateTransforms = {
Data,
Model,
Volume,
Representation
}
\ No newline at end of file
......@@ -5,28 +5,31 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { PluginStateTransform } from '../objects';
import { PluginStateObject as SO } from '../objects';
import { Task, RuntimeContext } from 'mol-task';
import { Model, Structure, ModelSymmetry, StructureSymmetry, QueryContext, StructureSelection as Sel, StructureQuery, Queries } from 'mol-model/structure';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import Expression from 'mol-script/language/expression';
import { compile } from 'mol-script/runtime/query/compiler';
import { MolScriptBuilder } from 'mol-script/language/builder';
import { StateObject } from 'mol-state';
import { PluginContext } from 'mol-plugin/context';
import { stringToWords } from 'mol-util/string';
import { volumeFromCcp4 } from 'mol-model-formats/volume/ccp4';
import { parsePDB } from 'mol-io/reader/pdb/parser';
import { Vec3 } from 'mol-math/linear-algebra';
import CIF from 'mol-io/reader/cif';
import { volumeFromDsn6 } from 'mol-model-formats/volume/dsn6';
import { volumeFromDensityServerData } from 'mol-model-formats/volume/density-server';
import { trajectoryFromMmCIF } from 'mol-model-formats/structure/mmcif';
import { parsePDB } from 'mol-io/reader/pdb/parser';
import { trajectoryFromPDB } from 'mol-model-formats/structure/pdb';
import { Model, ModelSymmetry, Queries, QueryContext, Structure, StructureQuery, StructureSelection as Sel, StructureSymmetry } from 'mol-model/structure';
import { Assembly } from 'mol-model/structure/model/properties/symmetry';
import { PluginContext } from 'mol-plugin/context';
import { MolScriptBuilder } from 'mol-script/language/builder';
import Expression from 'mol-script/language/expression';
import { compile } from 'mol-script/runtime/query/compiler';
import { StateObject } from 'mol-state';
import { RuntimeContext, Task } from 'mol-task';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import { stringToWords } from 'mol-util/string';
import { PluginStateObject as SO, PluginStateTransform } from '../objects';
export { TrajectoryFromMmCif }
export { TrajectoryFromMmCif };
export { TrajectoryFromPDB };
export { ModelFromTrajectory };
export { StructureFromModel };
export { StructureAssemblyFromModel };
export { StructureSymmetryFromModel };
export { StructureSelection };
export { StructureComplexElement };
export { CustomModelProperties };
type TrajectoryFromMmCif = typeof TrajectoryFromMmCif
const TrajectoryFromMmCif = PluginStateTransform.BuiltIn({
name: 'trajectory-from-mmcif',
......@@ -60,7 +63,6 @@ const TrajectoryFromMmCif = PluginStateTransform.BuiltIn({
});
export { TrajectoryFromPDB }
type TrajectoryFromPDB = typeof TrajectoryFromPDB
const TrajectoryFromPDB = PluginStateTransform.BuiltIn({
name: 'trajectory-from-pdb',
......@@ -80,7 +82,6 @@ const TrajectoryFromPDB = PluginStateTransform.BuiltIn({
});
export { ModelFromTrajectory }
const plus1 = (v: number) => v + 1, minus1 = (v: number) => v - 1;
type ModelFromTrajectory = typeof ModelFromTrajectory
const ModelFromTrajectory = PluginStateTransform.BuiltIn({
......@@ -106,7 +107,6 @@ const ModelFromTrajectory = PluginStateTransform.BuiltIn({
}
});
export { StructureFromModel }
type StructureFromModel = typeof StructureFromModel
const StructureFromModel = PluginStateTransform.BuiltIn({
name: 'structure-from-model',
......@@ -125,7 +125,6 @@ function structureDesc(s: Structure) {
return s.elementCount === 1 ? '1 element' : `${s.elementCount} elements`;
}
export { StructureAssemblyFromModel }
type StructureAssemblyFromModel = typeof StructureAssemblyFromModel
const StructureAssemblyFromModel = PluginStateTransform.BuiltIn({
name: 'structure-assembly-from-model',
......@@ -178,7 +177,6 @@ const StructureAssemblyFromModel = PluginStateTransform.BuiltIn({
}
});
export { StructureSymmetryFromModel }
type StructureSymmetryFromModel = typeof StructureSymmetryFromModel
const StructureSymmetryFromModel = PluginStateTransform.BuiltIn({
name: 'structure-symmetry-from-model',
......@@ -204,7 +202,6 @@ const StructureSymmetryFromModel = PluginStateTransform.BuiltIn({
}
});
export { StructureSelection }
type StructureSelection = typeof StructureSelection
const StructureSelection = PluginStateTransform.BuiltIn({
name: 'structure-selection',
......@@ -226,7 +223,6 @@ const StructureSelection = PluginStateTransform.BuiltIn({
}
});
export { StructureComplexElement }
namespace StructureComplexElement {
export type Types = 'atomic-sequence' | 'water' | 'atomic-het' | 'spheres'
}
......@@ -259,7 +255,6 @@ const StructureComplexElement = PluginStateTransform.BuiltIn({
}
});
export { CustomModelProperties }
type CustomModelProperties = typeof CustomModelProperties
const CustomModelProperties = PluginStateTransform.BuiltIn({
name: 'custom-model-properties',
......@@ -284,82 +279,3 @@ async function attachProps(model: Model, ctx: PluginContext, taskCtx: RuntimeCon
await p.attach(model).runInContext(taskCtx);
}
}
\ No newline at end of file
//
export { VolumeFromCcp4 }
type VolumeFromCcp4 = typeof VolumeFromCcp4
const VolumeFromCcp4 = PluginStateTransform.BuiltIn({
name: 'volume-from-ccp4',
display: { name: 'Volume from CCP4/MRC/MAP', description: 'Create Volume from CCP4/MRC/MAP data' },
from: SO.Format.Ccp4,
to: SO.Volume.Data,
params(a) {
return {
voxelSize: PD.Vec3(Vec3.create(1, 1, 1))
};
}
})({
apply({ a, params }) {
return Task.create('Create volume from CCP4/MRC/MAP', async ctx => {
const volume = await volumeFromCcp4(a.data, params).runInContext(ctx)
const props = { label: 'Volume' };
return new SO.Volume.Data(volume, props);
});
}
});
export { VolumeFromDsn6 }
type VolumeFromDsn6 = typeof VolumeFromDsn6
const VolumeFromDsn6 = PluginStateTransform.BuiltIn({
name: 'volume-from-dsn6',
display: { name: 'Volume from DSN6/BRIX', description: 'Create Volume from DSN6/BRIX data' },
from: SO.Format.Dsn6,
to: SO.Volume.Data,
params(a) {
return {
voxelSize: PD.Vec3(Vec3.create(1, 1, 1))
};
}
})({
apply({ a, params }) {
return Task.create('Create volume from DSN6/BRIX', async ctx => {
const volume = await volumeFromDsn6(a.data, params).runInContext(ctx)
const props = { label: 'Volume' };
return new SO.Volume.Data(volume, props);
});
}
});
export { VolumeFromDensityServerCif }
type VolumeFromDensityServerCif = typeof VolumeFromDensityServerCif
const VolumeFromDensityServerCif = PluginStateTransform.BuiltIn({
name: 'volume-from-density-server-cif',
display: { name: 'Volume from density-server CIF', description: 'Identify and create all separate models in the specified CIF data block' },
from: SO.Format.Cif,
to: SO.Volume.Data,
params(a) {
if (!a) {
return {
blockHeader: PD.makeOptional(PD.Text(void 0, { description: 'Header of the block to parse. If none is specifed, the 1st data block in the file is used.' }))
};
}
const blocks = a.data.blocks.slice(1); // zero block contains query meta-data
return {
blockHeader: PD.makeOptional(PD.Select(blocks[0] && blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]), { description: 'Header of the block to parse' }))
};
}
})({
isApplicable: a => a.data.blocks.length > 0,
apply({ a, params }) {
return Task.create('Parse density-server CIF', async ctx => {
const header = params.blockHeader || a.data.blocks[1].header; // zero block contains query meta-data
const block = a.data.blocks.find(b => b.header === header);
if (!block) throw new Error(`Data block '${[header]}' not found.`);
const densityServerCif = CIF.schema.densityServer(block)
const volume = await volumeFromDensityServerData(densityServerCif).runInContext(ctx)
const props = { label: densityServerCif.volume_data_3d_info.name.value(0), description: `${densityServerCif.volume_data_3d_info.name.value(0)}` };
return new SO.Volume.Data(volume, props);
});
}
});
\ No newline at end of file
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import CIF from 'mol-io/reader/cif';
import { Vec3 } from 'mol-math/linear-algebra';
import { volumeFromCcp4 } from 'mol-model-formats/volume/ccp4';
import { volumeFromDensityServerData } from 'mol-model-formats/volume/density-server';
import { volumeFromDsn6 } from 'mol-model-formats/volume/dsn6';
import { Task } from 'mol-task';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import { PluginStateObject as SO, PluginStateTransform } from '../objects';
import { VolumeStreamingBehavior as VolumeStreaming } from 'mol-plugin/behavior/dynamic/volume';
import { PluginContext } from 'mol-plugin/context';
import { StateTransformer } from 'mol-state';
import { VolumeData, VolumeIsoValue } from 'mol-model/volume';
import { BuiltInVolumeRepresentations } from 'mol-repr/volume/registry';
import { createTheme } from 'mol-theme/theme';
export { VolumeFromCcp4 };
export { VolumeFromDsn6 };
export { VolumeFromDensityServerCif };
type VolumeFromCcp4 = typeof VolumeFromCcp4
const VolumeFromCcp4 = PluginStateTransform.BuiltIn({
name: 'volume-from-ccp4',
display: { name: 'Volume from CCP4/MRC/MAP', description: 'Create Volume from CCP4/MRC/MAP data' },
from: SO.Format.Ccp4,
to: SO.Volume.Data,
params(a) {
return {
voxelSize: PD.Vec3(Vec3.create(1, 1, 1))
};
}
})({
apply({ a, params }) {
return Task.create('Create volume from CCP4/MRC/MAP', async ctx => {
const volume = await volumeFromCcp4(a.data, params).runInContext(ctx)
const props = { label: 'Volume' };
return new SO.Volume.Data(volume, props);
});
}
});
type VolumeFromDsn6 = typeof VolumeFromDsn6
const VolumeFromDsn6 = PluginStateTransform.BuiltIn({
name: 'volume-from-dsn6',
display: { name: 'Volume from DSN6/BRIX', description: 'Create Volume from DSN6/BRIX data' },
from: SO.Format.Dsn6,
to: SO.Volume.Data,
params(a) {
return {
voxelSize: PD.Vec3(Vec3.create(1, 1, 1))
};
}
})({
apply({ a, params }) {
return Task.create('Create volume from DSN6/BRIX', async ctx => {
const volume = await volumeFromDsn6(a.data, params).runInContext(ctx)
const props = { label: 'Volume' };
return new SO.Volume.Data(volume, props);
});
}
});
type VolumeFromDensityServerCif = typeof VolumeFromDensityServerCif
const VolumeFromDensityServerCif = PluginStateTransform.BuiltIn({
name: 'volume-from-density-server-cif',
display: { name: 'Volume from density-server CIF', description: 'Identify and create all separate models in the specified CIF data block' },
from: SO.Format.Cif,
to: SO.Volume.Data,
params(a) {
if (!a) {
return {
blockHeader: PD.makeOptional(PD.Text(void 0, { description: 'Header of the block to parse. If none is specifed, the 1st data block in the file is used.' }))
};
}
const blocks = a.data.blocks.slice(1); // zero block contains query meta-data
return {
blockHeader: PD.makeOptional(PD.Select(blocks[0] && blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]), { description: 'Header of the block to parse' }))
};
}
})({
isApplicable: a => a.data.blocks.length > 0,
apply({ a, params }) {
return Task.create('Parse density-server CIF', async ctx => {
const header = params.blockHeader || a.data.blocks[1].header; // zero block contains query meta-data
const block = a.data.blocks.find(b => b.header === header);
if (!block) throw new Error(`Data block '${[header]}' not found.`);
const densityServerCif = CIF.schema.densityServer(block)
const volume = await volumeFromDensityServerData(densityServerCif).runInContext(ctx)
const props = { label: densityServerCif.volume_data_3d_info.name.value(0), description: `${densityServerCif.volume_data_3d_info.name.value(0)}` };
return new SO.Volume.Data(volume, props);
});
}
});
export { VolumeStreamingBehavior }
type VolumeStreamingBehavior = typeof VolumeStreamingBehavior
const VolumeStreamingBehavior = PluginStateTransform.BuiltIn({
name: 'volume-streaming-behavior',
display: { name: 'Volume Streaming Behavior', description: 'Create Volume Streaming behavior.' },
from: SO.Molecule.Model,
to: VolumeStreaming.Obj,
params: VolumeStreaming.Params
})({
apply({ a, params }, plugin: PluginContext) {
const behavior = new VolumeStreaming.Behavior(plugin, params);
return new VolumeStreaming.Obj(behavior, { label: 'Volume Streaming' });
},
update({ b, newParams }) {
return Task.create('Update Volume Streaming', async _ => {
await b.data.update(newParams);
return StateTransformer.UpdateResult.Updated;
});
}
});
export { VolumeStreamingVisual }
type VolumeStreamingVisual = typeof VolumeStreamingVisual
const VolumeStreamingVisual = PluginStateTransform.BuiltIn({
name: 'volume-streaming-visual',
display: { name: 'Volume Streaming Visual' },
from: VolumeStreaming.Obj,
to: SO.Volume.Representation3D,
params: {
channel: PD.Select<keyof VolumeStreaming.ChannelData>('EM', [['EM', 'EM'], ['FO-FC', 'Fo-Fc'], ['2FO-FC', '2Fo-Fc']], { isHidden: true }),
level: PD.Text<VolumeStreaming.LevelType>('em')
}
})({
apply: ({ a, params }, plugin: PluginContext) => Task.create('Volume Representation', async ctx => {
const { data, props, theme } = createVolumeProps(a.data, params.channel, params.level)
const repr = BuiltInVolumeRepresentations.isosurface.factory({ webgl: plugin.canvas3d.webgl, ...plugin.volumeRepresentation.themeCtx }, BuiltInVolumeRepresentations.isosurface.getParams);
repr.setTheme(theme);
await repr.createOrUpdate(props, data).runInContext(ctx);
return new SO.Volume.Representation3D(repr, { label: params.level });
}),
update: ({ a, b, oldParams, newParams }) => Task.create('Volume Representation', async ctx => {
// TODO : check if params have changed
const { data, props, theme } = createVolumeProps(a.data, newParams.channel, newParams.level);
b.data.setTheme(theme);
await b.data.createOrUpdate(props, data).runInContext(ctx);
return StateTransformer.UpdateResult.Updated;
})
});
function createVolumeProps(streaming: VolumeStreaming.Behavior, channel: keyof VolumeStreaming.ChannelData, level: VolumeStreaming.LevelType) {
const data = streaming.currentData[channel] || VolumeData.Empty;
const { themeCtx } = streaming.ctx.volumeRepresentation;
const props = PD.getDefaultValues(BuiltInVolumeRepresentations.isosurface.getParams(themeCtx, data));
let isoValue: VolumeIsoValue;
if (level === 'em' && streaming.params.levels.name === 'em') {
isoValue = streaming.params.levels.params.isoValue;
} else if (level !== 'em' && streaming.params.levels.name === 'x-ray') {
isoValue = streaming.params.levels.params[level].isoValue;
} else {
throw new Error(`Unsupported iso level ${level}.`);
}
props.isoValue = isoValue;
const theme = createTheme(streaming.ctx.volumeRepresentation.themeCtx, { volume: data }, props);
return { data, props, theme };
}
\ No newline at end of file
......@@ -19,7 +19,7 @@ import { VisualContext } from 'mol-repr/visual';
import { NullLocation } from 'mol-model/location';
import { Lines } from 'mol-geo/geometry/lines/lines';
const IsoValueParam = PD.Conditioned(
export const IsoValueParam = PD.Conditioned(
VolumeIsoValue.relative(2),
{
'absolute': PD.Converted(
......
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Adapted from LiteMol.
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { LinkedList } from 'mol-data/generic';
export { LRUCache }
interface LRUCache<T> {
entries: LinkedList<LRUCache.Entry<T>>,
capacity: number
}
namespace LRUCache {
export interface Entry<T> {
key: string,
data: T
}
function entry<T>(key: string, data: T): Entry<T> {
return { key, data };
}
export function create<T>(capacity: number): LRUCache<T> {
return {
entries: LinkedList<Entry<T>>(),
capacity: Math.max(1, capacity)
};
}
export function get<T>(cache: LRUCache<T>, key: string) {
for (let e = cache.entries.first; e; e = e.next) {
if (e.value.key === key) {
cache.entries.remove(e);
cache.entries.addLast(e.value);
return e.value.data;
}
}
return void 0;
}
export function set<T>(cache: LRUCache<T>, key: string, data: T): T {
if (cache.entries.count >= cache.capacity) {
cache.entries.remove(cache.entries.first!);
}
cache.entries.addLast(entry(key, data));
return data;
}
}
\ 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