diff --git a/src/mol-plugin/state/actions/structure.ts b/src/mol-plugin/state/actions/structure.ts index 76b4b0d9f7c23b4a10c7154b44865a9ed7ab41be..714adecf217632e6c000e5e2344ff76866148005 100644 --- a/src/mol-plugin/state/actions/structure.ts +++ b/src/mol-plugin/state/actions/structure.ts @@ -69,8 +69,8 @@ export const GroProvider: DataFormatProvider<any> = { // const DownloadStructurePdbIdSourceOptions = PD.Group({ - supportProps: PD.asOptional(PD.Boolean(false)), - asTrajectory: PD.asOptional(PD.Boolean(false, { description: 'Load all entries into a single trajectory.' })) + supportProps: PD.Optional(PD.Boolean(false)), + asTrajectory: PD.Optional(PD.Boolean(false, { description: 'Load all entries into a single trajectory.' })) }); export { DownloadStructure }; @@ -97,7 +97,7 @@ const DownloadStructure = StateAction.build({ format: PD.Select('cif', [['cif', 'CIF'], ['pdb', 'PDB']]), isBinary: PD.Boolean(false), options: PD.Group({ - supportProps: PD.asOptional(PD.Boolean(false)) + supportProps: PD.Optional(PD.Boolean(false)) }) }, { isFlat: true }) }, { @@ -242,7 +242,7 @@ export const UpdateTrajectory = StateAction.build({ display: { name: 'Update Trajectory' }, params: { action: PD.Select<'advance' | 'reset'>('advance', [['advance', 'Advance'], ['reset', 'Reset']]), - by: PD.asOptional(PD.Numeric(1, { min: -1, max: 1, step: 1 })) + by: PD.Optional(PD.Numeric(1, { min: -1, max: 1, step: 1 })) } })(({ params, state }) => { const models = state.selectQ(q => q.ofTransformer(StateTransforms.Model.ModelFromTrajectory)); diff --git a/src/mol-plugin/state/animation/built-in.ts b/src/mol-plugin/state/animation/built-in.ts index 0c58a509ede0eee55be6d84fcd966070b612350c..308bc1bd6d07f909c3977287cf1a825719cc4b2f 100644 --- a/src/mol-plugin/state/animation/built-in.ts +++ b/src/mol-plugin/state/animation/built-in.ts @@ -113,6 +113,9 @@ export const AnimateAssemblyUnwind = PluginStateAnimation.create({ const update = state.build(); let changed = false; for (const r of reprs) { + // TODO: find a better way to handle this, perhaps add a different state object?? + if (r.transform.transformer === StateTransforms.Representation.StructureLabels3D) continue; + const unwinds = state.select(StateSelection.Generators.ofTransformer(StateTransforms.Representation.UnwindStructureAssemblyRepresentation3D, r.transform.ref)); if (unwinds.length > 0) continue; @@ -180,6 +183,9 @@ export const AnimateUnitsExplode = PluginStateAnimation.create({ const update = state.build(); let changed = false; for (const r of reprs) { + // TODO: find a better way to handle this, perhaps add a different state object?? + if (r.transform.transformer === StateTransforms.Representation.StructureLabels3D) continue; + const explodes = state.select(StateSelection.Generators.ofTransformer(StateTransforms.Representation.ExplodeStructureRepresentation3D, r.transform.ref)); if (explodes.length > 0) continue; diff --git a/src/mol-plugin/state/transforms/data.ts b/src/mol-plugin/state/transforms/data.ts index d04b01d59d334b9138b075f0e27dccfe6e1a796f..aa0554a568085ad5ac2cb6a93bc0259e33e39b4b 100644 --- a/src/mol-plugin/state/transforms/data.ts +++ b/src/mol-plugin/state/transforms/data.ts @@ -25,8 +25,8 @@ const Download = PluginStateTransform.BuiltIn({ to: [SO.Data.String, SO.Data.Binary], params: { url: PD.Text('https://www.ebi.ac.uk/pdbe/static/entry/1cbs_updated.cif', { description: 'Resource URL. Must be the same domain or support CORS.' }), - label: PD.asOptional(PD.Text('')), - isBinary: PD.asOptional(PD.Boolean(false, { description: 'If true, download data as binary (string otherwise)' })) + label: PD.Optional(PD.Text('')), + isBinary: PD.Optional(PD.Boolean(false, { description: 'If true, download data as binary (string otherwise)' })) } })({ apply({ params: p }, globalCtx: PluginContext) { @@ -58,10 +58,10 @@ const DownloadBlob = PluginStateTransform.BuiltIn({ sources: PD.ObjectList({ id: PD.Text('', { label: 'Unique ID' }), url: PD.Text('https://www.ebi.ac.uk/pdbe/static/entry/1cbs_updated.cif', { description: 'Resource URL. Must be the same domain or support CORS.' }), - isBinary: PD.asOptional(PD.Boolean(false, { description: 'If true, download data as binary (string otherwise)' })), - canFail: PD.asOptional(PD.Boolean(false, { description: 'Indicate whether the download can fail and not be included in the blob as a result.' })) + isBinary: PD.Optional(PD.Boolean(false, { description: 'If true, download data as binary (string otherwise)' })), + canFail: PD.Optional(PD.Boolean(false, { description: 'Indicate whether the download can fail and not be included in the blob as a result.' })) }, e => `${e.id}: ${e.url}`), - maxConcurrency: PD.asOptional(PD.Numeric(4, { min: 1, max: 12, step: 1 }, { description: 'The maximum number of concurrent downloads.' })) + maxConcurrency: PD.Optional(PD.Numeric(4, { min: 1, max: 12, step: 1 }, { description: 'The maximum number of concurrent downloads.' })) } })({ apply({ params }, plugin: PluginContext) { @@ -102,8 +102,8 @@ const ReadFile = PluginStateTransform.BuiltIn({ to: [SO.Data.String, SO.Data.Binary], params: { file: PD.File(), - label: PD.asOptional(PD.Text('')), - isBinary: PD.asOptional(PD.Boolean(false, { description: 'If true, open file as as binary (string otherwise)' })) + label: PD.Optional(PD.Text('')), + isBinary: PD.Optional(PD.Boolean(false, { description: 'If true, open file as as binary (string otherwise)' })) } })({ apply({ params: p }) { diff --git a/src/mol-plugin/state/transforms/misc.ts b/src/mol-plugin/state/transforms/misc.ts index 86f3d961125a06b0eacbb6aacc2e59865a9685a9..69aac3a93e405563d7c0a55c3b442439a25a5fff 100644 --- a/src/mol-plugin/state/transforms/misc.ts +++ b/src/mol-plugin/state/transforms/misc.ts @@ -18,7 +18,7 @@ const CreateGroup = PluginStateTransform.BuiltIn({ to: SO.Group, params: { label: PD.Text('Group'), - description: PD.asOptional(PD.Text('')) + description: PD.Optional(PD.Text('')) } })({ apply({ params }) { diff --git a/src/mol-plugin/state/transforms/model.ts b/src/mol-plugin/state/transforms/model.ts index 57dd2bfa15dfd28f4b3f0d1434e76492e6faa73b..dd54e9dc49b54ddf352dd5f4e6888dfe33615734 100644 --- a/src/mol-plugin/state/transforms/model.ts +++ b/src/mol-plugin/state/transforms/model.ts @@ -71,12 +71,12 @@ const TrajectoryFromMmCif = PluginStateTransform.BuiltIn({ params(a) { if (!a) { return { - blockHeader: PD.asOptional(PD.Text(void 0, { description: 'Header of the block to parse. If none is specifed, the 1st data block in the file is used.' })) + blockHeader: PD.Optional(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; return { - blockHeader: PD.asOptional(PD.Select(blocks[0] && blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]), { description: 'Header of the block to parse' })) + blockHeader: PD.Optional(PD.Select(blocks[0] && blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]), { description: 'Header of the block to parse' })) }; } })({ @@ -181,12 +181,12 @@ const StructureAssemblyFromModel = PluginStateTransform.BuiltIn({ to: SO.Molecule.Structure, params(a) { if (!a) { - return { id: PD.asOptional(PD.Text('', { label: 'Assembly Id', description: 'Assembly Id. Value \'deposited\' can be used to specify deposited asymmetric unit.' })) }; + return { id: PD.Optional(PD.Text('', { label: 'Assembly Id', description: 'Assembly Id. Value \'deposited\' can be used to specify deposited asymmetric unit.' })) }; } const model = a.data; const ids = model.symmetry.assemblies.map(a => [a.id, `${a.id}: ${stringToWords(a.details)}`] as [string, string]); ids.push(['deposited', 'Deposited']); - return { id: PD.asOptional(PD.Select(ids[0][0], ids, { label: 'Asm Id', description: 'Assembly Id' })) }; + return { id: PD.Optional(PD.Select(ids[0][0], ids, { label: 'Asm Id', description: 'Assembly Id' })) }; } })({ apply({ a, params }, plugin: PluginContext) { @@ -258,7 +258,7 @@ const StructureSelection = PluginStateTransform.BuiltIn({ to: SO.Molecule.Structure, params: { query: PD.Value<Expression>(MolScriptBuilder.struct.generator.all, { isHidden: true }), - label: PD.asOptional(PD.Text('', { isHidden: true })) + label: PD.Optional(PD.Text('', { isHidden: true })) } })({ apply({ a, params, cache }) { @@ -295,7 +295,7 @@ const UserStructureSelection = PluginStateTransform.BuiltIn({ to: SO.Molecule.Structure, params: { query: PD.ScriptExpression({ language: 'mol-script', expression: '(sel.atom.atom-groups :residue-test (= atom.resname ALA))' }), - label: PD.asOptional(PD.Text('')) + label: PD.Optional(PD.Text('')) } })({ apply({ a, params, cache }) { diff --git a/src/mol-plugin/state/transforms/representation.ts b/src/mol-plugin/state/transforms/representation.ts index d13614ea4eca4acbac15ed3fd564356a6edcabcc..b5c9e1a8257281547faf30b096c49b42558d1816 100644 --- a/src/mol-plugin/state/transforms/representation.ts +++ b/src/mol-plugin/state/transforms/representation.ts @@ -202,9 +202,13 @@ const StructureLabels3D = PluginStateTransform.BuiltIn({ target: PD.MappedStatic('residues', { 'elements': PD.Group({ }), 'residues': PD.Group({ }), - 'static-text': PD.Group({ value: PD.Text('') }, { isFlat: true }) + 'static-text': PD.Group({ + value: PD.Text(''), + size: PD.Optional(PD.Numeric(1, { min: 1, max: 1000, step: 0.1 })), + // TODO: this changes the position while rotated etc... fix + position: PD.Optional(Text.Params.attachment) + }, { isFlat: true }) }), - // PD.Select<'elements' | 'residues'>('residues', [['residues', 'Residues'], ['elements', 'Elements']]), options: PD.Group({ ...Text.Params, @@ -215,8 +219,9 @@ const StructureLabels3D = PluginStateTransform.BuiltIn({ }) } })({ - canAutoUpdate({ a, oldParams, newParams }) { - return newParams.target.name === 'static-text' || newParams.target.name === oldParams.target.name + canAutoUpdate({ oldParams, newParams }) { + return (oldParams.target.name === 'static-text' && newParams.target.name === 'static-text' && oldParams.target.params.value === newParams.target.params.value) + || newParams.target.name === oldParams.target.name; }, apply({ a, params }) { return Task.create('Structure Labels', async ctx => { diff --git a/src/mol-plugin/state/transforms/volume.ts b/src/mol-plugin/state/transforms/volume.ts index 399293a21148dd21333e76d1715e1c3ef964cb13..6861dc237dd801dfb0d7be565107e0840d545508 100644 --- a/src/mol-plugin/state/transforms/volume.ts +++ b/src/mol-plugin/state/transforms/volume.ts @@ -68,12 +68,12 @@ const VolumeFromDensityServerCif = PluginStateTransform.BuiltIn({ params(a) { if (!a) { return { - blockHeader: PD.asOptional(PD.Text(void 0, { description: 'Header of the block to parse. If none is specifed, the 1st data block in the file is used.' })) + blockHeader: PD.Optional(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.asOptional(PD.Select(blocks[0] && blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]), { description: 'Header of the block to parse' })) + blockHeader: PD.Optional(PD.Select(blocks[0] && blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]), { description: 'Header of the block to parse' })) }; } })({ diff --git a/src/mol-plugin/ui/controls/parameters.tsx b/src/mol-plugin/ui/controls/parameters.tsx index 8267741e1775ae52efb9eaed5aecd5708aa4099e..72135f24d2a4cca1b01a47a69b98830547f9e292 100644 --- a/src/mol-plugin/ui/controls/parameters.tsx +++ b/src/mol-plugin/ui/controls/parameters.tsx @@ -181,7 +181,8 @@ export class NumberInputControl extends React.PureComponent<ParamProps<PD.Numeri export class NumberRangeControl extends SimpleParam<PD.Numeric> { onChange = (v: number) => { this.update(v); } renderControl() { - return <Slider value={this.props.value} min={this.props.param.min!} max={this.props.param.max!} + const value = typeof this.props.value === 'undefined' ? this.props.param.defaultValue : this.props.value; + return <Slider value={value} min={this.props.param.min!} max={this.props.param.max!} step={this.props.param.step} onChange={this.onChange} disabled={this.props.isDisabled} onEnter={this.props.onEnter} /> } } diff --git a/src/mol-plugin/util/structure-labels.ts b/src/mol-plugin/util/structure-labels.ts index af3b335de97a4acf11cf66241e6736800de73812..c6e944b29219ac96fed56f47556ad3885780ea36 100644 --- a/src/mol-plugin/util/structure-labels.ts +++ b/src/mol-plugin/util/structure-labels.ts @@ -50,20 +50,25 @@ function getLabelsShape(ctx: RuntimeContext, data: LabelsData, props: PD.Values< const boundaryHelper = new BoundaryHelper(); function getLabelData(structure: Structure, params: StateTransformer.Params<StructureLabels3D>): LabelsData { if (params.target.name === 'static-text') { - return getLabelDataStatic(structure, params.target.params.value); + return getLabelDataStatic(structure, params.target.params.value, params.target.params.size || 1, params.target.params.position || 'middle-center'); } else { return getLabelDataComputed(structure, params.target.name); } } -function getLabelDataStatic(structure: Structure, text: string): LabelsData { +function getLabelDataStatic(structure: Structure, text: string, size: number, position: Text.Params['attachment']['defaultValue']): LabelsData { const boundary = structure.boundary.sphere; + let oX = 0, oY = 0; + if (position.indexOf('left') >= 0) oX = -boundary.radius; + if (position.indexOf('right') >= 0) oX = boundary.radius; + if (position.indexOf('top') >= 0) oY = boundary.radius; + if (position.indexOf('bottom') >= 0) oY = -boundary.radius; return { texts: [text], - positions: [boundary.center], - sizes: [1], - depths: [boundary.radius] + positions: [Vec3.add(Vec3.zero(), boundary.center, Vec3.create(oX, oY, 0))], + sizes: [size], + depths: [boundary.radius + Math.sqrt(oX * oX + oY * oY)] }; } diff --git a/src/mol-util/param-definition.ts b/src/mol-util/param-definition.ts index e2ce230f31737f4c2a3c2336218f1145ab3c589d..a7f08f3cf1a3d8227703292d4c4078aeb862e499 100644 --- a/src/mol-util/param-definition.ts +++ b/src/mol-util/param-definition.ts @@ -36,9 +36,10 @@ export namespace ParamDefinition { type: T['type'] } - export function asOptional<T>(p: Base<T>): Base<T | undefined> { - p.isOptional = true; - return p; + export function Optional<T>(p: Base<T>): Base<T | undefined> { + const ret = { ...p }; + ret.isOptional = true; + return ret; } export interface Value<T> extends Base<T> {