From 624dce3b6931b673b6eb42924a98b3275b2d959a Mon Sep 17 00:00:00 2001 From: Alexander Rose <alexander.rose@weirdbyte.de> Date: Mon, 11 Feb 2019 07:56:49 -0800 Subject: [PATCH] wip isovalue param --- src/mol-plugin/ui/controls/parameters.tsx | 36 +++++++++++++ src/mol-repr/volume/isosurface-mesh.ts | 61 +++++++++++++++++++---- src/mol-repr/volume/representation.ts | 1 - src/mol-util/param-definition.ts | 16 +++++- 4 files changed, 100 insertions(+), 14 deletions(-) diff --git a/src/mol-plugin/ui/controls/parameters.tsx b/src/mol-plugin/ui/controls/parameters.tsx index 7d26c184c..ed890ef13 100644 --- a/src/mol-plugin/ui/controls/parameters.tsx +++ b/src/mol-plugin/ui/controls/parameters.tsx @@ -47,6 +47,7 @@ function controlFor(param: PD.Any): ParamControl | undefined { case 'number': return typeof param.min !== 'undefined' && typeof param.max !== 'undefined' ? NumberRangeControl : NumberInputControl; case 'converted': return ConvertedControl; + case 'conditioned': return ConditionedControl; case 'multi-select': return MultiSelectControl; case 'color': return ColorControl; case 'color-scale': return ColorScaleControl; @@ -456,6 +457,41 @@ export class MappedControl extends React.PureComponent<ParamProps<PD.Mapped<any> } } +export class ConditionedControl extends React.PureComponent<ParamProps<PD.Conditioned<any, any, any>>> { + change(value: PD.Conditioned<any, any, any>['defaultValue'] ) { + this.props.onChange({ name: this.props.name, param: this.props.param, value }); + } + + onChangeCondition: ParamOnChange = e => { + this.change(this.props.param.conditionedValue(this.props.value, e.value)); + } + + onChangeParam: ParamOnChange = e => { + this.change(e.value); + } + + render() { + const value = this.props.value; + const condition = this.props.param.conditionForValue(value) as string + const param = this.props.param.conditionParams[condition]; + const label = this.props.param.label || camelCaseToWords(this.props.name); + const Conditioned = controlFor(param); + + const select = <SelectControl param={this.props.param.select} + isDisabled={this.props.isDisabled} onChange={this.onChangeCondition} onEnter={this.props.onEnter} + name={`${label} Kind`} value={condition} /> + + if (!Conditioned) { + return select; + } + + return <div> + {select} + <Conditioned param={param} value={value} name={label} onChange={this.onChangeParam} onEnter={this.props.onEnter} isDisabled={this.props.isDisabled} /> + </div> + } +} + export class ConvertedControl extends React.PureComponent<ParamProps<PD.Converted<any, any>>> { onChange: ParamOnChange = e => { this.props.onChange({ diff --git a/src/mol-repr/volume/isosurface-mesh.ts b/src/mol-repr/volume/isosurface-mesh.ts index 94c720038..778b82946 100644 --- a/src/mol-repr/volume/isosurface-mesh.ts +++ b/src/mol-repr/volume/isosurface-mesh.ts @@ -5,7 +5,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { VolumeData } from 'mol-model/volume' +import { VolumeData, VolumeIsoValue } from 'mol-model/volume' import { VolumeVisual, VolumeRepresentation, VolumeRepresentationProvider } from './representation'; import { EmptyLoci } from 'mol-model/loci'; import { ParamDefinition as PD } from 'mol-util/param-definition'; @@ -19,9 +19,30 @@ import { VisualContext } from 'mol-repr/visual'; import { NullLocation } from 'mol-model/location'; import { Lines } from 'mol-geo/geometry/lines/lines'; -interface VolumeIsosurfaceProps { - isoValue: number +const IsoValueParam = PD.Conditioned( + VolumeIsoValue.relative(VolumeData.Empty.dataStats, 2), + { + 'absolute': PD.Converted( + (v: VolumeIsoValue) => VolumeIsoValue.toAbsolute(v).absoluteValue, + (v: number) => VolumeIsoValue.absolute(VolumeData.Empty.dataStats, v), + PD.Numeric(0, { min: -1, max: 1, step: 0.01 }) + ), + 'relative': PD.Converted( + (v: VolumeIsoValue) => VolumeIsoValue.toRelative(v).relativeValue, + (v: number) => VolumeIsoValue.relative(VolumeData.Empty.dataStats, v), + PD.Numeric(0, { min: -1, max: 1, step: 0.01 }) + ) + }, + (v: VolumeIsoValue) => v.kind === 'absolute' ? 'absolute' : 'relative', + (v: VolumeIsoValue, c: 'absolute' | 'relative') => c === 'absolute' ? VolumeIsoValue.toAbsolute(v) : VolumeIsoValue.toRelative(v) +) +type IsoValueParam = typeof IsoValueParam + +export const VolumeIsosurfaceParams = { + isoValue: IsoValueParam } +export type VolumeIsosurfaceParams = typeof VolumeIsosurfaceParams +export type VolumeIsosurfaceProps = PD.Values<VolumeIsosurfaceParams> // @@ -29,7 +50,7 @@ export async function createVolumeIsosurfaceMesh(ctx: VisualContext, volume: Vol ctx.runtime.update({ message: 'Marching cubes...' }); const surface = await computeMarchingCubesMesh({ - isoLevel: props.isoValue, + isoLevel: VolumeIsoValue.toAbsolute(props.isoValue).absoluteValue, scalarField: volume.data }, mesh).runAsChild(ctx.runtime); @@ -43,7 +64,7 @@ export async function createVolumeIsosurfaceMesh(ctx: VisualContext, volume: Vol export const IsosurfaceMeshParams = { ...Mesh.Params, - isoValue: PD.Numeric(0.22, { min: -1, max: 1, step: 0.01 }), + ...VolumeIsosurfaceParams } export type IsosurfaceMeshParams = typeof IsosurfaceMeshParams @@ -66,11 +87,10 @@ export function IsosurfaceMeshVisual(): VolumeVisual<IsosurfaceMeshParams> { export async function createVolumeIsosurfaceWireframe(ctx: VisualContext, volume: VolumeData, theme: Theme, props: VolumeIsosurfaceProps, lines?: Lines) { ctx.runtime.update({ message: 'Marching cubes...' }); - const params = { - isoLevel: props.isoValue, + const wireframe = await computeMarchingCubesLines({ + isoLevel: VolumeIsoValue.toAbsolute(props.isoValue).absoluteValue, scalarField: volume.data - } - const wireframe = await computeMarchingCubesLines(params, lines).runAsChild(ctx.runtime) + }, lines).runAsChild(ctx.runtime) const transform = VolumeData.getGridToCartesianTransform(volume); Lines.transformImmediate(wireframe, transform) @@ -80,7 +100,7 @@ export async function createVolumeIsosurfaceWireframe(ctx: VisualContext, volume export const IsosurfaceWireframeParams = { ...Lines.Params, - isoValue: PD.Numeric(0.22, { min: -1, max: 1, step: 0.01 }), + ...VolumeIsosurfaceParams } export type IsosurfaceWireframeParams = typeof IsosurfaceWireframeParams @@ -114,7 +134,26 @@ export const IsosurfaceParams = { } export type IsosurfaceParams = typeof IsosurfaceParams export function getIsosurfaceParams(ctx: ThemeRegistryContext, volume: VolumeData) { - return PD.clone(IsosurfaceParams) + const p = PD.clone(IsosurfaceParams) + const { min, max, mean, sigma } = volume.dataStats + p.isoValue = PD.Conditioned( + VolumeIsoValue.relative(volume.dataStats, 2), + { + 'absolute': PD.Converted( + (v: VolumeIsoValue) => VolumeIsoValue.toAbsolute(v).absoluteValue, + (v: number) => VolumeIsoValue.absolute(volume.dataStats, v), + PD.Numeric(mean, { min, max, step: sigma / 100 }) + ), + 'relative': PD.Converted( + (v: VolumeIsoValue) => VolumeIsoValue.toRelative(v).relativeValue, + (v: number) => VolumeIsoValue.relative(volume.dataStats, v), + PD.Numeric(2, { min: -10, max: 10, step: 0.001 }) + ) + }, + (v: VolumeIsoValue) => v.kind === 'absolute' ? 'absolute' : 'relative', + (v: VolumeIsoValue, c: 'absolute' | 'relative') => c === 'absolute' ? VolumeIsoValue.toAbsolute(v) : VolumeIsoValue.toRelative(v) + ) + return p } export type IsosurfaceRepresentation = VolumeRepresentation<IsosurfaceParams> diff --git a/src/mol-repr/volume/representation.ts b/src/mol-repr/volume/representation.ts index c38081926..0a68fe847 100644 --- a/src/mol-repr/volume/representation.ts +++ b/src/mol-repr/volume/representation.ts @@ -205,7 +205,6 @@ export type VolumeRepresentationProvider<P extends VolumeParams> = Representatio export const VolumeParams = { ...BaseGeometry.Params, - isoValue: PD.Numeric(0.22, { min: -1, max: 1, step: 0.01 }), } export type VolumeParams = typeof VolumeParams diff --git a/src/mol-util/param-definition.ts b/src/mol-util/param-definition.ts index ec68757b0..746c9a62b 100644 --- a/src/mol-util/param-definition.ts +++ b/src/mol-util/param-definition.ts @@ -190,7 +190,7 @@ export namespace ParamDefinition { export interface Converted<T, C> extends Base<T> { type: 'converted', converted: Any, - /** converts from props value to display value */ + /** converts from prop value to display value */ fromValue(v: T): C, /** converts from display value to prop value */ toValue(v: C): T @@ -199,7 +199,19 @@ export namespace ParamDefinition { return { type: 'converted', defaultValue: toValue(converted.defaultValue), converted, fromValue, toValue }; } - export type Any = Value<any> | Select<any> | MultiSelect<any> | Boolean | Text | Color | Vec3 | Numeric | FileParam | Interval | LineGraph | ColorScale<any> | Group<any> | Mapped<any> | Converted<any, any> + export interface Conditioned<T, P extends Base<T>, C = { [k: string]: P }> extends Base<T> { + type: 'conditioned', + select: Select<string>, + conditionParams: C + conditionForValue(v: T): keyof C + conditionedValue(v: T, condition: keyof C): T, + } + export function Conditioned<T, P extends Base<T>, C = { [k: string]: P }>(defaultValue: T, conditionParams: C, conditionForValue: (v: T) => keyof C, conditionedValue: (v: T, condition: keyof C) => T): Conditioned<T, P, C> { + const options = Object.keys(conditionParams).map(k => [k, k]) as [string, string][]; + return { type: 'conditioned', select: Select<string>(conditionForValue(defaultValue) as string, options), defaultValue, conditionParams, conditionForValue, conditionedValue }; + } + + export type Any = Value<any> | Select<any> | MultiSelect<any> | Boolean | Text | Color | Vec3 | Numeric | FileParam | Interval | LineGraph | ColorScale<any> | Group<any> | Mapped<any> | Converted<any, any> | Conditioned<any, any, any> export type Params = { [k: string]: Any } export type Values<T extends Params> = { [k in keyof T]: T[k]['defaultValue'] } -- GitLab