From 0e91aa521ec2bba0377c953210bc5729e1e516f6 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Tue, 25 Feb 2020 17:39:34 -0800 Subject: [PATCH] added IntervalControl, use 'step' for precision, allow 'range' in Vec3 --- src/mol-plugin-ui/controls/parameters.tsx | 60 +++++++++++++++---- .../dynamic/volume-streaming/behavior.ts | 4 +- src/mol-plugin/state/representation/model.ts | 6 +- src/mol-util/param-definition.ts | 8 +-- 4 files changed, 59 insertions(+), 19 deletions(-) diff --git a/src/mol-plugin-ui/controls/parameters.tsx b/src/mol-plugin-ui/controls/parameters.tsx index 49363d5cd..f211570e8 100644 --- a/src/mol-plugin-ui/controls/parameters.tsx +++ b/src/mol-plugin-ui/controls/parameters.tsx @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2020 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> @@ -19,6 +19,7 @@ import { _Props, _State } from '../base'; import { legendFor } from './legend'; import { Legend as LegendData } from '../../mol-util/legend'; import { CombinedColorControl, ColorValueOption, ColorOptions } from './color'; +import { getPrecision } from '../../mol-util/number'; export interface ParameterControlsProps<P extends PD.Params = PD.Params> { params: P, @@ -214,17 +215,20 @@ export class NumberInputControl extends React.PureComponent<ParamProps<PD.Numeri state = { value: '0' }; update = (value: number) => { + const p = getPrecision(this.props.param.step || 0.01) + value = parseFloat(value.toFixed(p)) this.props.onChange({ param: this.props.param, name: this.props.name, value }); } render() { const placeholder = this.props.param.label || camelCaseToWords(this.props.name); const label = this.props.param.label || camelCaseToWords(this.props.name); + const p = getPrecision(this.props.param.step || 0.01) return <div className='msp-control-row'> <span title={this.props.param.description}>{label}</span> <div> <NumericInput - value={this.props.value} onEnter={this.props.onEnter} placeholder={placeholder} + value={parseFloat(this.props.value.toFixed(p))} onEnter={this.props.onEnter} placeholder={placeholder} isDisabled={this.props.isDisabled} onChange={this.update} /> </div> </div>; @@ -306,10 +310,45 @@ export class SelectControl extends SimpleParam<PD.Select<string | number>> { } } -export class IntervalControl extends SimpleParam<PD.Interval> { - onChange = (v: [number, number]) => { this.update(v); } - renderControl() { - return <span>interval TODO</span>; +export class IntervalControl extends React.PureComponent<ParamProps<PD.Interval>, { isExpanded: boolean }> { + state = { isExpanded: false } + + components = { + 0: PD.Numeric(0, { step: this.props.param.step }, { label: 'Min' }), + 1: PD.Numeric(0, { step: this.props.param.step }, { label: 'Max' }) + } + + change(value: PD.MultiSelect<any>['defaultValue']) { + this.props.onChange({ name: this.props.name, param: this.props.param, value }); + } + + componentChange: ParamOnChange = ({ name, value }) => { + const v = [...this.props.value]; + v[+name] = value; + this.change(v); + } + + toggleExpanded = (e: React.MouseEvent<HTMLButtonElement>) => { + this.setState({ isExpanded: !this.state.isExpanded }); + e.currentTarget.blur(); + } + + render() { + const v = this.props.value; + const label = this.props.param.label || camelCaseToWords(this.props.name); + const p = getPrecision(this.props.param.step || 0.01) + const value = `[${v[0].toFixed(p)}, ${v[1].toFixed(p)}]`; + return <> + <div className='msp-control-row'> + <span>{label}</span> + <div> + <button onClick={this.toggleExpanded}>{value}</button> + </div> + </div> + <div className='msp-control-offset' style={{ display: this.state.isExpanded ? 'block' : 'none' }}> + <ParameterControls params={this.components} values={v} onChange={this.componentChange} onEnter={this.props.onEnter} /> + </div> + </>; } } @@ -399,9 +438,9 @@ export class Vec3Control extends React.PureComponent<ParamProps<PD.Vec3>, { isEx state = { isExpanded: false } components = { - 0: PD.Numeric(0, void 0, { label: (this.props.param.fieldLabels && this.props.param.fieldLabels.x) || 'X' }), - 1: PD.Numeric(0, void 0, { label: (this.props.param.fieldLabels && this.props.param.fieldLabels.y) || 'Y' }), - 2: PD.Numeric(0, void 0, { label: (this.props.param.fieldLabels && this.props.param.fieldLabels.z) || 'Z' }) + 0: PD.Numeric(0, { step: this.props.param.step }, { label: (this.props.param.fieldLabels && this.props.param.fieldLabels.x) || 'X' }), + 1: PD.Numeric(0, { step: this.props.param.step }, { label: (this.props.param.fieldLabels && this.props.param.fieldLabels.y) || 'Y' }), + 2: PD.Numeric(0, { step: this.props.param.step }, { label: (this.props.param.fieldLabels && this.props.param.fieldLabels.z) || 'Z' }) } change(value: PD.MultiSelect<any>['defaultValue']) { @@ -422,7 +461,8 @@ export class Vec3Control extends React.PureComponent<ParamProps<PD.Vec3>, { isEx render() { const v = this.props.value; const label = this.props.param.label || camelCaseToWords(this.props.name); - const value = `[${v[0].toFixed(2)}, ${v[1].toFixed(2)}, ${v[2].toFixed(2)}]`; + const p = getPrecision(this.props.param.step || 0.01) + const value = `[${v[0].toFixed(p)}, ${v[1].toFixed(p)}, ${v[2].toFixed(p)}]`; return <> <div className='msp-control-row'> <span>{label}</span> diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts index ed27bf3ca..85e873d15 100644 --- a/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts +++ b/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts @@ -93,8 +93,8 @@ export namespace VolumeStreaming { }, { description: 'Static box defined by cartesian coords.', isFlat: true }), 'selection-box': PD.Group({ radius: PD.Numeric(5, { min: 0, max: 50, step: 0.5 }, { description: 'Radius in \u212B within which the volume is shown.' }), - bottomLeft: PD.Vec3(Vec3.create(0, 0, 0), { isHidden: true }), - topRight: PD.Vec3(Vec3.create(0, 0, 0), { isHidden: true }), + bottomLeft: PD.Vec3(Vec3.create(0, 0, 0), {}, { isHidden: true }), + topRight: PD.Vec3(Vec3.create(0, 0, 0), {}, { isHidden: true }), }, { description: 'Box around last-interacted element.', isFlat: true }), 'cell': PD.Group({}), // 'auto': PD.Group({ }), // TODO based on camera distance/active selection/whatever, show whole structure or slice. diff --git a/src/mol-plugin/state/representation/model.ts b/src/mol-plugin/state/representation/model.ts index 0c25c5313..e658c3e42 100644 --- a/src/mol-plugin/state/representation/model.ts +++ b/src/mol-plugin/state/representation/model.ts @@ -53,14 +53,14 @@ export namespace ModelStructureRepresentation { radius: PD.Numeric(5) }, { isFlat: true }), 'symmetry': PD.Group({ - ijkMin: PD.Vec3(Vec3.create(-1, -1, -1), { label: 'Min IJK', fieldLabels: { x: 'I', y: 'J', z: 'K' } }), - ijkMax: PD.Vec3(Vec3.create(1, 1, 1), { label: 'Max IJK', fieldLabels: { x: 'I', y: 'J', z: 'K' } }) + ijkMin: PD.Vec3(Vec3.create(-1, -1, -1), { step: 1 }, { label: 'Min IJK', fieldLabels: { x: 'I', y: 'J', z: 'K' } }), + ijkMax: PD.Vec3(Vec3.create(1, 1, 1), { step: 1 }, { label: 'Max IJK', fieldLabels: { x: 'I', y: 'J', z: 'K' } }) }, { isFlat: true }), 'symmetry-assembly': PD.Group({ generators: PD.ObjectList({ operators: PD.ObjectList({ index: PD.Select(0, operatorOptions), - shift: PD.Vec3(Vec3(), { label: 'IJK', fieldLabels: { x: 'I', y: 'J', z: 'K' } }) + shift: PD.Vec3(Vec3(), { step: 1 }, { label: 'IJK', fieldLabels: { x: 'I', y: 'J', z: 'K' } }) }, e => `${e.index + 1}_${e.shift.map(a => a + 5).join('')}`, { defaultValue: [] as { index: number, shift: Vec3 }[] }), diff --git a/src/mol-util/param-definition.ts b/src/mol-util/param-definition.ts index edcf9300c..b2ccbedce 100644 --- a/src/mol-util/param-definition.ts +++ b/src/mol-util/param-definition.ts @@ -111,11 +111,11 @@ export namespace ParamDefinition { return setInfo<Color>({ type: 'color', defaultValue }, info) } - export interface Vec3 extends Base<Vec3Data> { + export interface Vec3 extends Base<Vec3Data>, Range { type: 'vec3' } - export function Vec3(defaultValue: Vec3Data, info?: Info): Vec3 { - return setInfo<Vec3>({ type: 'vec3', defaultValue }, info) + export function Vec3(defaultValue: Vec3Data, range?: { min?: number, max?: number, step?: number }, info?: Info): Vec3 { + return setInfo<Vec3>(setRange({ type: 'vec3', defaultValue }, range), info) } export interface FileParam extends Base<File> { @@ -149,7 +149,7 @@ export namespace ParamDefinition { */ step?: number } - function setRange<T extends Numeric | Interval>(p: T, range?: { min?: number, max?: number, step?: number }) { + function setRange<T extends Numeric | Interval | Vec3>(p: T, range?: { min?: number, max?: number, step?: number }) { if (!range) return p; if (typeof range.min !== 'undefined') p.min = range.min; if (typeof range.max !== 'undefined') p.max = range.max; -- GitLab