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

support for streaming multiple em volumes, limit to associate maps

parent 6ac790e0
No related branches found
No related tags found
No related merge requests found
......@@ -55,13 +55,27 @@ export namespace VolumeStreaming {
}
export function createParams(data?: VolumeServerInfo.Data, defaultView?: ViewTypes, binding?: typeof DefaultBindings) {
const map = new Map<string, VolumeServerInfo.EntryData>()
if (data) data.entries.forEach(d => map.set(d.dataId, d))
const names = data ? data.entries.map(d => [d.dataId, d.dataId] as [string, string]) : []
const defaultKey = data ? data.entries[0].dataId : ''
return {
entry: PD.Mapped<EntryParams>(defaultKey, names, name => PD.Group(createEntryParams(map.get(name)!, defaultView, data && data.structure))),
bindings: PD.Value(binding || DefaultBindings, { isHidden: true }),
}
}
export type EntryParamDefinition = typeof createEntryParams extends (...args: any[]) => (infer T) ? T : never
export type EntryParams = EntryParamDefinition extends PD.Params ? PD.Values<EntryParamDefinition> : {}
export function createEntryParams(entryData?: VolumeServerInfo.EntryData, defaultView?: ViewTypes, structure?: Structure) {
// fake the info
const info = data || { kind: 'em', header: { sampling: [fakeSampling], availablePrecisions: [{ precision: 0, maxVoxels: 0 }] }, emDefaultContourLevel: VolumeIsoValue.relative(0) };
const box = (data && data.structure.boundary.box) || Box3D.empty();
const info = entryData || { kind: 'em', header: { sampling: [fakeSampling], availablePrecisions: [{ precision: 0, maxVoxels: 0 }] }, emDefaultContourLevel: VolumeIsoValue.relative(0) };
const box = (structure && structure.boundary.box) || Box3D.empty();
return {
view: PD.MappedStatic(defaultView || (info.kind === 'em' ? 'cell' : 'selection-box'), {
'off': PD.Group({}),
'off': PD.Group<{}>({}),
'box': PD.Group({
bottomLeft: PD.Vec3(box.min),
topRight: PD.Vec3(box.max),
......@@ -85,7 +99,6 @@ export namespace VolumeStreaming {
'fo-fc(+ve)': channelParam('Fo-Fc(+ve)', Color(0x33BB33), VolumeIsoValue.relative(3), info.header.sampling[0].valuesInfo[1]),
'fo-fc(-ve)': channelParam('Fo-Fc(-ve)', Color(0xBB3333), VolumeIsoValue.relative(-3), info.header.sampling[0].valuesInfo[1]),
}, { isFlat: true }),
bindings: PD.Value(binding || DefaultBindings, { isHidden: true }),
};
}
......@@ -118,11 +131,16 @@ export namespace VolumeStreaming {
public params: Params = {} as any;
private lastLoci: StructureElement.Loci | EmptyLoci = EmptyLoci;
private ref: string = '';
public infoMap: Map<string, VolumeServerInfo.EntryData>
channels: Channels = {}
public get info () {
return this.infoMap.get(this.params.entry.name)!
}
private async queryData(box?: Box3D) {
let url = urlCombine(this.info.serverUrl, `${this.info.kind}/${this.info.dataId.toLowerCase()}`);
let url = urlCombine(this.data.serverUrl, `${this.info.kind}/${this.info.dataId.toLowerCase()}`);
if (box) {
const { min: a, max: b } = box;
......@@ -132,7 +150,7 @@ export namespace VolumeStreaming {
} else {
url += `/cell`;
}
url += `?detail=${this.params.detailLevel}`;
url += `?detail=${this.params.entry.params.detailLevel}`;
let data = LRUCache.get(this.cache, url);
if (data) {
......@@ -165,26 +183,32 @@ export namespace VolumeStreaming {
const block = parsed.result.blocks[i];
const densityServerCif = CIF.schema.densityServer(block);
const volume = await this.plugin.runTask(await volumeFromDensityServerData(densityServerCif));
const volume = await this.plugin.runTask(volumeFromDensityServerData(densityServerCif));
(ret as any)[block.header as any] = volume;
}
return ret;
}
private updateDynamicBox(box: Box3D) {
if (this.params.view.name !== 'selection-box') return;
if (this.params.entry.params.view.name !== 'selection-box') return;
const state = this.plugin.state.dataState;
const newParams: Params = {
...this.params,
entry: {
name: this.params.entry.name,
params: {
...this.params.entry.params,
view: {
name: 'selection-box' as 'selection-box',
params: {
radius: this.params.view.params.radius,
radius: this.params.entry.params.view.params.radius,
bottomLeft: box.min,
topRight: box.max
}
}
}
}
};
const update = state.build().to(this.ref).update(newParams);
......@@ -214,7 +238,7 @@ export namespace VolumeStreaming {
this.subscribeObservable(this.plugin.behaviors.interaction.click, ({ current, buttons, modifiers }) => {
if (!Binding.match((this.params.bindings && this.params.bindings.clickVolumeAroundOnly) || DefaultBindings.clickVolumeAroundOnly, buttons, modifiers)) return;
if (this.params.view.name !== 'selection-box') {
if (this.params.entry.params.view.name !== 'selection-box') {
this.lastLoci = this.getNormalizedLoci(current.loci);
} else {
this.updateInteraction(current);
......@@ -272,34 +296,34 @@ export namespace VolumeStreaming {
}
async update(params: Params) {
const switchedToSelection = params.view.name === 'selection-box' && this.params && this.params.view && this.params.view.name !== 'selection-box';
const switchedToSelection = params.entry.params.view.name === 'selection-box' && this.params && this.params.entry && this.params.entry.params && this.params.entry.params.view && this.params.entry.params.view.name !== 'selection-box';
this.params = params;
let box: Box3D | undefined = void 0, emptyData = false;
switch (params.view.name) {
switch (params.entry.params.view.name) {
case 'off':
emptyData = true;
break;
case 'box':
box = Box3D.create(params.view.params.bottomLeft, params.view.params.topRight);
box = Box3D.create(params.entry.params.view.params.bottomLeft, params.entry.params.view.params.topRight);
emptyData = Box3D.volume(box) < 0.0001;
break;
case 'selection-box': {
if (switchedToSelection) {
box = this.getBoxFromLoci(this.lastLoci) || Box3D.empty();
} else {
box = Box3D.create(Vec3.clone(params.view.params.bottomLeft), Vec3.clone(params.view.params.topRight));
box = Box3D.create(Vec3.clone(params.entry.params.view.params.bottomLeft), Vec3.clone(params.entry.params.view.params.topRight));
}
const r = params.view.params.radius;
const r = params.entry.params.view.params.radius;
emptyData = Box3D.volume(box) < 0.0001;
Box3D.expand(box, box, Vec3.create(r, r, r));
break;
}
case 'cell':
box = this.info.kind === 'x-ray'
? this.info.structure.boundary.box
? this.data.structure.boundary.box
: void 0;
break;
}
......@@ -308,7 +332,7 @@ export namespace VolumeStreaming {
if (!data) return false;
const info = params.channels as ChannelsInfo;
const info = params.entry.params.channels as ChannelsInfo;
if (this.info.kind === 'x-ray') {
this.channels['2fo-fc'] = this.createChannel(data['2FO-FC'] || VolumeData.One, info['2fo-fc'], this.info.header.sampling[0].valuesInfo[0]);
......@@ -333,14 +357,17 @@ export namespace VolumeStreaming {
}
getDescription() {
if (this.params.view.name === 'selection-box') return 'Selection';
if (this.params.view.name === 'box') return 'Static Box';
if (this.params.view.name === 'cell') return 'Cell';
if (this.params.entry.params.view.name === 'selection-box') return 'Selection';
if (this.params.entry.params.view.name === 'box') return 'Static Box';
if (this.params.entry.params.view.name === 'cell') return 'Cell';
return '';
}
constructor(public plugin: PluginContext, public info: VolumeServerInfo.Data) {
constructor(public plugin: PluginContext, public data: VolumeServerInfo.Data) {
super(plugin, {} as any);
this.infoMap = new Map<string, VolumeServerInfo.EntryData>()
this.data.entries.forEach(info => this.infoMap.set(info.dataId, info))
}
}
}
\ No newline at end of file
......@@ -2,6 +2,7 @@
* Copyright (c) 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 { PluginStateObject } from '../../../state/objects';
......@@ -12,13 +13,16 @@ export class VolumeServerInfo extends PluginStateObject.Create<VolumeServerInfo.
export namespace VolumeServerInfo {
export type Kind = 'x-ray' | 'em'
export interface Data {
serverUrl: string,
export interface EntryData {
kind: Kind,
// for em, the EMDB access code, for x-ray, the PDB id
dataId: string,
header: VolumeServerHeader,
emDefaultContourLevel?: VolumeIsoValue,
}
export interface Data {
serverUrl: string,
entries: EntryData[],
structure: Structure
}
}
......
......@@ -14,7 +14,7 @@ import { urlCombine } from '../../../../mol-util/url';
import { createIsoValueParam } from '../../../../mol-repr/volume/isosurface';
import { VolumeIsoValue } from '../../../../mol-model/volume';
import { StateAction, StateObject, StateTransformer } from '../../../../mol-state';
import { getStreamingMethod, getId, getContourLevel, getEmdbId } from './util';
import { getStreamingMethod, getIds, getContourLevel, getEmdbIds } from './util';
import { VolumeStreaming } from './behavior';
import { VolumeRepresentation3DHelpers } from '../../../../mol-plugin/state/transforms/representation';
import { BuiltInVolumeRepresentations } from '../../../../mol-repr/volume/registry';
......@@ -22,15 +22,24 @@ import { createTheme } from '../../../../mol-theme/theme';
import { Box3D } from '../../../../mol-math/geometry';
import { Vec3 } from '../../../../mol-math/linear-algebra';
function addEntry(entries: InfoEntryProps[], method: VolumeServerInfo.Kind, dataId: string, emDefaultContourLevel: number) {
entries.push({
source: method === 'em'
? { name: 'em', params: { isoValue: VolumeIsoValue.absolute(emDefaultContourLevel || 0) } }
: { name: 'x-ray', params: { } },
dataId
})
}
export const InitVolumeStreaming = StateAction.build({
display: { name: 'Volume Streaming' },
from: SO.Molecule.Structure,
params(a) {
const method = getStreamingMethod(a && a.data);
const id = getId(a && a.data);
const ids = getIds(method, a && a.data);
return {
method: PD.Select<VolumeServerInfo.Kind>(method, [['em', 'EM'], ['x-ray', 'X-Ray']]),
id: PD.Text(id),
entries: PD.ObjectList({ id: PD.Text(ids[0] || '') }, ({ id }) => id, { defaultValue: ids.map(id => ({ id })) }),
serverUrl: PD.Text('https://ds.litemol.org'),
defaultView: PD.Select<VolumeStreaming.ViewTypes>(method === 'em' ? 'cell' : 'selection-box', VolumeStreaming.ViewTypeOptions as any),
behaviorRef: PD.Text('', { isHidden: true }),
......@@ -40,23 +49,35 @@ export const InitVolumeStreaming = StateAction.build({
},
isApplicable: (a) => a.data.models.length === 1
})(({ ref, state, params }, plugin: PluginContext) => Task.create('Volume Streaming', async taskCtx => {
let dataId = params.id.toLowerCase(), emDefaultContourLevel: number | undefined;
const entries: InfoEntryProps[] = []
for (let i = 0, il = params.entries.length; i < il; ++i) {
let dataId = params.entries[i].id.toLowerCase()
let emDefaultContourLevel: number | undefined;
if (params.method === 'em') {
await taskCtx.update('Getting EMDB info...');
// if pdb ids are given for method 'em', get corresponding emd ids
// and continue the loop
if (!dataId.toUpperCase().startsWith('EMD')) {
dataId = await getEmdbId(plugin, taskCtx, dataId)
await taskCtx.update('Getting EMDB info...');
const emdbIds = await getEmdbIds(plugin, taskCtx, dataId)
for (let j = 0, jl = emdbIds.length; j < jl; ++j) {
const emdbId = emdbIds[j]
const contourLevel = await getContourLevel(params.emContourProvider, plugin, taskCtx, emdbId)
addEntry(entries, params.method, emdbId, contourLevel || 0)
}
continue;
}
const contourLevel = await getContourLevel(params.emContourProvider, plugin, taskCtx, dataId);
emDefaultContourLevel = contourLevel || 0;
emDefaultContourLevel = await getContourLevel(params.emContourProvider, plugin, taskCtx, dataId);
}
addEntry(entries, params.method, dataId, emDefaultContourLevel || 0)
}
const infoTree = state.build().to(ref)
.apply(CreateVolumeStreamingInfo, {
serverUrl: params.serverUrl,
source: params.method === 'em'
? { name: 'em', params: { isoValue: VolumeIsoValue.absolute(emDefaultContourLevel || 0) } }
: { name: 'x-ray', params: { } },
dataId
entries
});
const infoObj = await state.updateTree(infoTree).runInContext(taskCtx);
......@@ -78,15 +99,19 @@ export const InitVolumeStreaming = StateAction.build({
export const BoxifyVolumeStreaming = StateAction.build({
display: { name: 'Boxify Volume Streaming', description: 'Make the current box permanent.' },
from: VolumeStreaming,
isApplicable: (a) => a.data.params.view.name === 'selection-box'
isApplicable: (a) => a.data.params.entry.params.view.name === 'selection-box'
})(({ a, ref, state }, plugin: PluginContext) => {
const params = a.data.params;
if (params.view.name !== 'selection-box') return;
const box = Box3D.create(Vec3.clone(params.view.params.bottomLeft), Vec3.clone(params.view.params.topRight));
const r = params.view.params.radius;
if (params.entry.params.view.name !== 'selection-box') return;
const box = Box3D.create(Vec3.clone(params.entry.params.view.params.bottomLeft), Vec3.clone(params.entry.params.view.params.topRight));
const r = params.entry.params.view.params.radius;
Box3D.expand(box, box, Vec3.create(r, r, r));
const newParams: VolumeStreaming.Params = {
...params,
entry: {
name: params.entry.name,
params: {
...params.entry.params,
view: {
name: 'box' as 'box',
params: {
......@@ -94,10 +119,23 @@ export const BoxifyVolumeStreaming = StateAction.build({
topRight: box.max
}
}
}
}
};
return state.updateTree(state.build().to(ref).update(newParams));
});
const InfoEntryParams = {
dataId: PD.Text(''),
source: PD.MappedStatic('x-ray', {
'em': PD.Group({
isoValue: createIsoValueParam(VolumeIsoValue.relative(1))
}),
'x-ray': PD.Group({ })
})
}
type InfoEntryProps = PD.Values<typeof InfoEntryParams>
export { CreateVolumeStreamingInfo }
type CreateVolumeStreamingInfo = typeof CreateVolumeStreamingInfo
const CreateVolumeStreamingInfo = PluginStateTransform.BuiltIn({
......@@ -108,30 +146,34 @@ const CreateVolumeStreamingInfo = PluginStateTransform.BuiltIn({
params(a) {
return {
serverUrl: PD.Text('https://ds.litemol.org'),
source: PD.MappedStatic('x-ray', {
'em': PD.Group({
isoValue: createIsoValueParam(VolumeIsoValue.relative(1))
}),
'x-ray': PD.Group({ })
entries: PD.ObjectList<InfoEntryProps>(InfoEntryParams, ({ dataId }) => dataId, {
defaultValue: [{ dataId: '', source: { name: 'x-ray', params: {} } }]
}),
dataId: PD.Text('')
};
}
})({
apply: ({ a, params }, plugin: PluginContext) => Task.create('', async taskCtx => {
const dataId = params.dataId;
const emDefaultContourLevel = params.source.name === 'em' ? params.source.params.isoValue : VolumeIsoValue.relative(1);
const entries: VolumeServerInfo.EntryData[] = []
for (let i = 0, il = params.entries.length; i < il; ++i) {
const e = params.entries[i]
const dataId = e.dataId;
const emDefaultContourLevel = e.source.name === 'em' ? e.source.params.isoValue : VolumeIsoValue.relative(1);
await taskCtx.update('Getting server header...');
const header = await plugin.fetch<VolumeServerHeader>({ url: urlCombine(params.serverUrl, `${params.source.name}/${dataId.toLocaleLowerCase()}`), type: 'json' }).runInContext(taskCtx);
const data: VolumeServerInfo.Data = {
serverUrl: params.serverUrl,
const header = await plugin.fetch<VolumeServerHeader>({ url: urlCombine(params.serverUrl, `${e.source.name}/${dataId.toLocaleLowerCase()}`), type: 'json' }).runInContext(taskCtx);
entries.push({
dataId,
kind: params.source.name,
kind: e.source.name,
header,
emDefaultContourLevel,
emDefaultContourLevel
})
}
const data: VolumeServerInfo.Data = {
serverUrl: params.serverUrl,
entries,
structure: a.data
};
return new VolumeServerInfo(data, { label: `Volume Server: ${dataId}` });
return new VolumeServerInfo(data, { label: 'Volume Server', description: `${entries.map(e => e.dataId). join(', ')}` });
})
});
......@@ -147,17 +189,25 @@ const CreateVolumeStreamingBehavior = PluginStateTransform.BuiltIn({
}
})({
canAutoUpdate: ({ oldParams, newParams }) => {
return oldParams.view === newParams.view
|| newParams.view.name === 'selection-box'
|| newParams.view.name === 'off';
return oldParams.entry.params.view === newParams.entry.params.view
|| newParams.entry.params.view.name === 'selection-box'
|| newParams.entry.params.view.name === 'off';
},
apply: ({ a, params }, plugin: PluginContext) => Task.create('Volume streaming', async _ => {
const behavior = new VolumeStreaming.Behavior(plugin, a.data);
await behavior.update(params);
return new VolumeStreaming(behavior, { label: 'Volume Streaming', description: behavior.getDescription() });
}),
update({ b, newParams }) {
update({ a, b, oldParams, newParams }) {
return Task.create('Update Volume Streaming', async _ => {
if (oldParams.entry.name !== newParams.entry.name) {
if ('em' in newParams.entry.params.channels) {
const { emDefaultContourLevel } = b.data.infoMap.get(newParams.entry.name)!
if (emDefaultContourLevel) {
newParams.entry.params.channels['em'].isoValue = emDefaultContourLevel
}
}
}
const ret = await b.data.update(newParams) ? StateTransformer.UpdateResult.Updated : StateTransformer.UpdateResult.Unchanged;
b.description = b.data.getDescription();
return ret;
......
......@@ -5,7 +5,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Structure } from '../../../../mol-model/structure';
import { Structure, Model } from '../../../../mol-model/structure';
import { VolumeServerInfo } from './model';
import { PluginContext } from '../../../../mol-plugin/context';
import { RuntimeContext } from '../../../../mol-task';
......@@ -25,20 +25,34 @@ export function getStreamingMethod(s?: Structure, defaultKind: VolumeServerInfo.
return 'x-ray';
}
export function getId(s?: Structure): string {
if (!s) return ''
/** Returns EMD ID when available, otherwise falls back to PDB ID */
export function getEmIds(model: Model): string[] {
const ids: string[] = []
if (model.sourceData.kind !== 'mmCIF') return [ model.entryId ]
const model = s.models[0]
if (model.sourceData.kind !== 'mmCIF') return ''
const { db_id, db_name, content_type } = model.sourceData.data.pdbx_database_related
if (!db_name.isDefined) return [ model.entryId ]
const d = model.sourceData.data
for (let i = 0, il = d.pdbx_database_related._rowCount; i < il; ++i) {
if (d.pdbx_database_related.db_name.value(i).toUpperCase() === 'EMDB') {
return d.pdbx_database_related.db_id.value(i)
for (let i = 0, il = db_name.rowCount; i < il; ++i) {
if (db_name.value(i).toUpperCase() === 'EMDB' && content_type.value(i) === 'associated EM volume') {
ids.push(db_id.value(i))
}
}
return ids
}
return s.models.length > 0 ? s.models[0].entryId : ''
export function getXrayIds(model: Model): string[] {
return [ model.entryId ]
}
export function getIds(method: VolumeServerInfo.Kind, s?: Structure): string[] {
if (!s || !s.models.length) return []
const model = s.models[0]
switch (method) {
case 'em': return getEmIds(model)
case 'x-ray': return getXrayIds(model)
}
}
export async function getContourLevel(provider: 'wwpdb' | 'pdbe', plugin: PluginContext, taskCtx: RuntimeContext, emdbId: string) {
......@@ -71,21 +85,21 @@ export async function getContourLevelPdbe(plugin: PluginContext, taskCtx: Runtim
return contourLevel;
}
export async function getEmdbId(plugin: PluginContext, taskCtx: RuntimeContext, pdbId: string) {
export async function getEmdbIds(plugin: PluginContext, taskCtx: RuntimeContext, pdbId: string) {
// TODO: parametrize to a differnt URL? in plugin settings perhaps
const summary = await plugin.fetch({ url: `https://www.ebi.ac.uk/pdbe/api/pdb/entry/summary/${pdbId}`, type: 'json' }).runInContext(taskCtx);
const summaryEntry = summary && summary[pdbId];
let emdbId: string;
let emdbIds: string[] = [];
if (summaryEntry && summaryEntry[0] && summaryEntry[0].related_structures) {
const emdb = summaryEntry[0].related_structures.filter((s: any) => s.resource === 'EMDB');
const emdb = summaryEntry[0].related_structures.filter((s: any) => s.resource === 'EMDB' && s.relationship === 'associated EM volume');
if (!emdb.length) {
throw new Error(`No related EMDB entry found for '${pdbId}'.`);
}
emdbId = emdb[0].accession;
emdbIds.push(...emdb.map((e: { accession: string }) => e.accession));
} else {
throw new Error(`No related EMDB entry found for '${pdbId}'.`);
}
return emdbId
return emdbIds
}
\ No newline at end of file
......@@ -61,16 +61,22 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
}
changeIso = (name: string, value: number, isRelative: boolean) => {
const old = this.props.params;
const old = this.props.params as VolumeStreaming.Params
this.newParams({
...old,
entry: {
name: old.entry.name,
params: {
...old.entry.params,
channels: {
...old.channels,
...old.entry.params.channels,
[name]: {
...old.channels[name],
...(old.entry.params.channels as any)[name],
isoValue: isRelative ? VolumeIsoValue.relative(value) : VolumeIsoValue.absolute(value)
}
}
}
}
});
};
......@@ -78,13 +84,19 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
const old = this.props.params;
this.newParams({
...old,
entry: {
name: old.entry.name,
params: {
...old.entry.params,
channels: {
...old.channels,
...old.entry.params.channels,
[name]: {
...old.channels[name],
...(old.entry.params.channels as any)[name],
[param]: value
}
}
}
}
});
};
......@@ -94,17 +106,31 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
: VolumeIsoValue.toAbsolute(channel.isoValue, stats) }
}
changeOption: ParamOnChange = ({ value }) => {
changeOption: ParamOnChange = ({ name, value }) => {
const old = this.props.params as VolumeStreaming.Params
if (name === 'entry') {
this.newParams({
...old,
entry: {
name: value,
params: old.entry.params,
}
});
} else {
const b = (this.props.b as VolumeStreaming).data;
const isEM = b.info.kind === 'em';
const isRelative = value.params.isRelative;
const sampling = b.info.header.sampling[0];
const old = this.props.params as VolumeStreaming.Params, oldChannels = old.channels as any;
const oldChannels = old.entry.params.channels as any;
const oldView = old.view.name === value.name
? old.view.params
: (this.props.info.params as VolumeStreaming.ParamDefinition).view.map(value.name).defaultValue;
const oldView = old.entry.params.view.name === value.name
? old.entry.params.view.params
: (((this.props.info.params as VolumeStreaming.ParamDefinition)
.entry.map(old.entry.name) as PD.Group<VolumeStreaming.EntryParamDefinition>)
.params as VolumeStreaming.EntryParamDefinition)
.view.map(value.name).defaultValue;
const viewParams = { ...oldView };
if (value.name === 'selection-box') {
......@@ -116,6 +142,10 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
this.newParams({
...old,
entry: {
name: old.entry.name,
params: {
...old.entry.params,
view: {
name: value.name,
params: viewParams
......@@ -128,7 +158,10 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
'fo-fc(+ve)': this.convert(oldChannels['fo-fc(+ve)'], sampling.valuesInfo[1], isRelative),
'fo-fc(-ve)': this.convert(oldChannels['fo-fc(-ve)'], sampling.valuesInfo[1], isRelative)
}
}
}
});
}
};
render() {
......@@ -139,50 +172,54 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
const pivot = isEM ? 'em' : '2fo-fc';
const params = this.props.params as VolumeStreaming.Params;
const isRelative = ((params.channels as any)[pivot].isoValue as VolumeIsoValue).kind === 'relative';
const detailLevel = ((this.props.info.params as VolumeStreaming.ParamDefinition)
.entry.map(params.entry.name) as PD.Group<VolumeStreaming.EntryParamDefinition>).params.detailLevel
const isRelative = ((params.entry.params.channels as any)[pivot].isoValue as VolumeIsoValue).kind === 'relative';
const sampling = b.info.header.sampling[0];
// TODO: factor common things out
const OptionsParams = {
view: PD.MappedStatic(params.view.name, {
entry: PD.Select(params.entry.name, b.data.entries.map(info => [info.dataId, info.dataId] as [string, string])),
view: PD.MappedStatic(params.entry.params.view.name, {
'off': PD.Group({}, { description: 'Display off.' }),
'box': PD.Group({
bottomLeft: PD.Vec3(Vec3.zero()),
topRight: PD.Vec3(Vec3.zero()),
detailLevel: this.props.info.params.detailLevel,
detailLevel,
isRelative: PD.Boolean(isRelative, { description: 'Use relative or absolute iso values.' })
}, { description: 'Static box defined by cartesian coords.' }),
'selection-box': PD.Group({
radius: PD.Numeric(5, { min: 0, max: 50, step: 0.5 }),
detailLevel: this.props.info.params.detailLevel,
detailLevel,
isRelative: PD.Boolean(isRelative, { description: 'Use relative or absolute iso values.' })
}, { description: 'Box around last-interacted element.' }),
'cell': PD.Group({
detailLevel: this.props.info.params.detailLevel,
detailLevel,
isRelative: PD.Boolean(isRelative, { description: 'Use relative or absolute iso values.' })
}, { description: 'Box around the structure\'s bounding box.' }),
// 'auto': PD.Group({ }), // TODO based on camera distance/active selection/whatever, show whole structure or slice.
}, { options: [['off', 'Off'], ['box', 'Bounded Box'], ['selection-box', 'Surroundings'], ['cell', 'Whole Structure']] })
};
const options = {
entry: params.entry.name,
view: {
name: params.view.name,
name: params.entry.params.view.name,
params: {
detailLevel: params.detailLevel,
radius: (params.view.params as any).radius,
bottomLeft: (params.view.params as any).bottomLeft,
topRight: (params.view.params as any).topRight,
detailLevel: params.entry.params.detailLevel,
radius: (params.entry.params.view.params as any).radius,
bottomLeft: (params.entry.params.view.params as any).bottomLeft,
topRight: (params.entry.params.view.params as any).topRight,
isRelative
}
}
};
return <>
{!isEM && <Channel label='2Fo-Fc' name='2fo-fc' channels={params.channels} changeIso={this.changeIso} changeParams={this.changeParams} isRelative={isRelative} params={this.props} stats={sampling.valuesInfo[0]} />}
{!isEM && <Channel label='Fo-Fc(+ve)' name='fo-fc(+ve)' channels={params.channels} changeIso={this.changeIso} changeParams={this.changeParams} isRelative={isRelative} params={this.props} stats={sampling.valuesInfo[1]} />}
{!isEM && <Channel label='Fo-Fc(-ve)' name='fo-fc(-ve)' channels={params.channels} changeIso={this.changeIso} changeParams={this.changeParams} isRelative={isRelative} params={this.props} stats={sampling.valuesInfo[1]} />}
{isEM && <Channel label='EM' name='em' channels={params.channels} changeIso={this.changeIso} changeParams={this.changeParams} isRelative={isRelative} params={this.props} stats={sampling.valuesInfo[0]} />}
{!isEM && <Channel label='2Fo-Fc' name='2fo-fc' channels={params.entry.params.channels} changeIso={this.changeIso} changeParams={this.changeParams} isRelative={isRelative} params={this.props} stats={sampling.valuesInfo[0]} />}
{!isEM && <Channel label='Fo-Fc(+ve)' name='fo-fc(+ve)' channels={params.entry.params.channels} changeIso={this.changeIso} changeParams={this.changeParams} isRelative={isRelative} params={this.props} stats={sampling.valuesInfo[1]} />}
{!isEM && <Channel label='Fo-Fc(-ve)' name='fo-fc(-ve)' channels={params.entry.params.channels} changeIso={this.changeIso} changeParams={this.changeParams} isRelative={isRelative} params={this.props} stats={sampling.valuesInfo[1]} />}
{isEM && <Channel label='EM' name='em' channels={params.entry.params.channels} changeIso={this.changeIso} changeParams={this.changeParams} isRelative={isRelative} params={this.props} stats={sampling.valuesInfo[0]} />}
<ParameterControls onChange={this.changeOption} params={OptionsParams} values={options} onEnter={this.props.events.onEnter} />
</>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment