diff --git a/src/apps/state-docs/pd-to-md.ts b/src/apps/state-docs/pd-to-md.ts index 13e876ed7789f2705de7572823793dd7f4992e58..8e618fecbd3fafd8cc5d641d75536dffc8da9625 100644 --- a/src/apps/state-docs/pd-to-md.ts +++ b/src/apps/state-docs/pd-to-md.ts @@ -21,6 +21,7 @@ function paramInfo(param: PD.Any, offset: number): string { case 'color': return 'Color as 0xrrggbb'; case 'color-list': return `A list of colors as 0xrrggbb`; case 'vec3': return `3D vector [x, y, z]`; + case 'mat4': return `4x4 transformation matrix`; case 'file': return `JavaScript File Handle`; case 'file-list': return `JavaScript FileList Handle`; case 'select': return `One of ${oToS(param.options)}`; diff --git a/src/mol-plugin-state/transforms/model.ts b/src/mol-plugin-state/transforms/model.ts index 5272c204e550abb6944941297971270c118e7d29..e64f38951e59f4deb8fe0c87c4ab2e0fbe0c92df 100644 --- a/src/mol-plugin-state/transforms/model.ts +++ b/src/mol-plugin-state/transforms/model.ts @@ -310,7 +310,7 @@ const StructureCoordinateSystem = PluginStateTransform.BuiltIn({ angle: PD.Numeric(0, { min: -180, max: 180, step: 0.1 }), translation: PD.Vec3(Vec3.create(0, 0, 0)), }, { isFlat: true }), - matrix: PD.Value(Mat4.identity(), { isHidden: true }) + matrix: PD.Mat4(Mat4.identity()) }, { label: 'Kind' }) } })({ @@ -353,7 +353,7 @@ const TransformStructureConformation = PluginStateTransform.BuiltIn({ angle: PD.Numeric(0, { min: -180, max: 180, step: 0.1 }), translation: PD.Vec3(Vec3.create(0, 0, 0)), }, { isFlat: true }), - matrix: PD.Value(Mat4.identity(), { isHidden: true }) + matrix: PD.Mat4(Mat4.identity()) }, { label: 'Kind' }) } })({ diff --git a/src/mol-plugin-ui/controls/parameters.tsx b/src/mol-plugin-ui/controls/parameters.tsx index 955b640b14dcdc884ed307266b9ef2edb0ac323e..d03b7449074a0fe759611d2c0663e4da3f2469b2 100644 --- a/src/mol-plugin-ui/controls/parameters.tsx +++ b/src/mol-plugin-ui/controls/parameters.tsx @@ -6,7 +6,7 @@ */ import * as React from 'react'; -import { Vec2, Vec3 } from '../../mol-math/linear-algebra'; +import { Vec2, Vec3, Mat4 } from '../../mol-math/linear-algebra'; import { PluginContext } from '../../mol-plugin/context'; import { Color } from '../../mol-util/color'; import { ColorListOptions, ColorListOptionsScale, ColorListOptionsSet, getColorListFromName, ColorListName } from '../../mol-util/color/lists'; @@ -178,6 +178,7 @@ function controlFor(param: PD.Any): ParamControl | undefined { case 'color': return CombinedColorControl; case 'color-list': return ColorListControl; case 'vec3': return Vec3Control; + case 'mat4': return Mat4Control; case 'file': return FileControl; case 'file-list': return FileListControl; case 'select': return SelectControl; @@ -719,6 +720,55 @@ export class Vec3Control extends React.PureComponent<ParamProps<PD.Vec3>, { isEx } } +export class Mat4Control extends React.PureComponent<ParamProps<PD.Mat4>, { isExpanded: boolean }> { + state = { isExpanded: false } + + components = { + 0: PD.Numeric(0), + 1: PD.Numeric(0), + 2: PD.Numeric(0), + 3: PD.Numeric(0), + 4: PD.Numeric(0), + 5: PD.Numeric(0), + 6: PD.Numeric(0), + 7: PD.Numeric(0), + 8: PD.Numeric(0), + 9: PD.Numeric(0), + 10: PD.Numeric(0), + 11: PD.Numeric(0), + 12: PD.Numeric(0), + 13: PD.Numeric(0), + 14: PD.Numeric(0), + 15: PD.Numeric(0), + } + + change(value: PD.MultiSelect<any>['defaultValue']) { + this.props.onChange({ name: this.props.name, param: this.props.param, value }); + } + + componentChange: ParamOnChange = ({ name, value }) => { + const v = Mat4.copy(Mat4.zero(), 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); + return <> + <ControlRow label={label} control={<button onClick={this.toggleExpanded} disabled={this.props.isDisabled}>{'4\u00D74 Matrix'}</button>} /> + {this.state.isExpanded && <div className='msp-control-offset'> + <ParameterControls params={this.components} values={v} onChange={this.componentChange} onEnter={this.props.onEnter} /> + </div>} + </>; + } +} + export class FileControl extends React.PureComponent<ParamProps<PD.FileParam>> { change(value: File) { this.props.onChange({ name: this.props.name, param: this.props.param, value }); diff --git a/src/mol-util/param-definition.ts b/src/mol-util/param-definition.ts index 32b01b0e1c5ae2e53d481c70c5ad81e72e190fcc..b008677758a4837244ee0749b0b498cd2566266b 100644 --- a/src/mol-util/param-definition.ts +++ b/src/mol-util/param-definition.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 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 Alexander Rose <alexander.rose@weirdbyte.de> * @author David Sehnal <david.sehnal@gmail.com> @@ -7,7 +7,7 @@ import { Color as ColorData } from './color'; import { shallowEqualObjects } from './index'; -import { Vec2 as Vec2Data, Vec3 as Vec3Data } from '../mol-math/linear-algebra'; +import { Vec2 as Vec2Data, Vec3 as Vec3Data, Mat4 as Mat4Data, EPSILON } from '../mol-math/linear-algebra'; import { deepClone } from './object'; import { Script as ScriptData } from '../mol-script/script'; import { Legend } from './legend'; @@ -140,6 +140,13 @@ export namespace ParamDefinition { return setInfo<Vec3>(setRange({ type: 'vec3', defaultValue }, range), info) } + export interface Mat4 extends Base<Mat4Data> { + type: 'mat4' + } + export function Mat4(defaultValue: Mat4Data, info?: Info): Mat4 { + return setInfo<Mat4>({ type: 'mat4', defaultValue }, info) + } + export interface FileParam extends Base<File> { type: 'file' accept?: string @@ -290,7 +297,7 @@ export namespace ParamDefinition { } export type Any = - | Value<any> | Select<any> | MultiSelect<any> | BooleanParam | Text | Color | Vec3 | Numeric | FileParam | FileListParam | Interval | LineGraph + | Value<any> | Select<any> | MultiSelect<any> | BooleanParam | Text | Color | Vec3 | Mat4 | Numeric | FileParam | FileListParam | Interval | LineGraph | ColorList | Group<any> | Mapped<any> | Converted<any, any> | Conditioned<any, any, any> | Script | ObjectList export type Params = { [k: string]: Any } @@ -385,6 +392,8 @@ export namespace ParamDefinition { return true; } else if (p.type === 'vec3') { return Vec3Data.equals(a, b); + } else if (p.type === 'mat4') { + return Mat4Data.areEqual(a, b, EPSILON); } else if (p.type === 'script') { const u = a as Script['defaultValue'], v = b as Script['defaultValue']; return u.language === v.language && u.expression === v.expression;