diff --git a/docs/docs/plugin/managers/markdown-extensions.md b/docs/docs/plugin/managers/markdown-extensions.md index 87aadd19d..5bfcf2b15 100644 --- a/docs/docs/plugin/managers/markdown-extensions.md +++ b/docs/docs/plugin/managers/markdown-extensions.md @@ -23,11 +23,11 @@ Generally, the command should be URL encoded, e.g., `a b` => `a%20b` (in JS, `en - `focus-refs=ref1,ref2,...` - On click, focuses nodes with the provided refs - `highlight-refs=ref1,ref2,...` - On mouse over, highlights the provided refs - `query=...&lang=...&action=highlight,focus&focus-radius=...` - - `query` is an expression (e.g., `resn HEM` when using PyMol syntax) - - (optional) `lang` is one of `mol-script` (default), `pymol`, `vmd`, `jmol` - - (optional) `action` is an array of `highlight` (default), `focus` (multiple actions can be specified) - - (optional) `focus-radius` is extra distance applied when focusing the selection (default is `3`) - - Example: `[HEM](!query%3Dresn%20HEM%26lang%3Dpymol%26action%3Dhighlight%2Cfocus)` highlights or focuses the HEM residue (the command must be URL encoded because it contains spaces and possibly other special characters) + - `query` is an expression (e.g., `resn HEM` when using PyMol syntax) + - (optional) `lang` is one of `mol-script` (default), `pymol`, `vmd`, `jmol` + - (optional) `action` is an array of `highlight` (default), `focus` (multiple actions can be specified) + - (optional) `focus-radius` is extra distance applied when focusing the selection (default is `3`) + - Example: `[HEM](!query=resn%20HEM%26lang=pymol&action=highlight,focus)` highlights or focuses the HEM residue (the query must be URL encoded because it contains spaces and possibly other special characters) - `play-audio=src`, `toggle-audio[=str]`, `stop-audio`, `pause-audio` - Audio playback support ## Custom Content @@ -37,11 +37,11 @@ Extends Markdown Image syntax to support expressions of the form `![alt](!c1=v1& ### Built-in Custom Content - `color-swatch=color` - Renders a box with the provided color - Color palettes: - - `color-palette-name=name` - Renders a gradient with the provided named color palette (see `mol-util/color/lists.ts` for supported color schemes) - - `color-palette-colors=color1,color2` - Renders a gradient with the provided colors - - `color-palette-width=CCS-value` - Specifies the width of the element, defaults to `150px` - - `color-palette-height=CCS-value` - Specified the height of the element, defaults to `0.5em` - - `color-palette-discrete` - Renders discrete color list instead of interpolating + - `color-palette-name=name` - Renders a gradient with the provided named color palette (see `mol-util/color/lists.ts` for supported color schemes) + - `color-palette-colors=color1,color2` - Renders a gradient with the provided colors + - `color-palette-width=CCS-value` - Specifies the width of the element, defaults to `150px` + - `color-palette-height=CCS-value` - Specified the height of the element, defaults to `0.5em` + - `color-palette-discrete` - Renders discrete color list instead of interpolating ## Example diff --git a/src/mol-plugin-state/manager/focus-camera/focus-object.ts b/src/mol-plugin-state/manager/focus-camera/focus-object.ts index 48c1271cf..45e918bfd 100644 --- a/src/mol-plugin-state/manager/focus-camera/focus-object.ts +++ b/src/mol-plugin-state/manager/focus-camera/focus-object.ts @@ -13,7 +13,7 @@ import { Loci } from '../../../mol-model/loci'; import { Structure } from '../../../mol-model/structure'; import { PluginContext } from '../../../mol-plugin/context'; import { PluginState } from '../../../mol-plugin/state'; -import { StateObject, StateTransform } from '../../../mol-state'; +import { StateObjectCell, StateSelection, StateTransform } from '../../../mol-state'; import { PluginStateObject } from '../../objects'; @@ -65,7 +65,7 @@ export function getCellBoundingSphere(plugin: PluginContext, cellRef: StateTrans /** Push bounding spheres within cell `cellRef` to `out`. If a cell does not define bounding spheres, collect bounding spheres from subtree. */ function collectCellBoundingSpheres(out: Sphere3D[], plugin: PluginContext, cellRef: StateTransform.Ref): Sphere3D[] { const cell = plugin.state.data.cells.get(cellRef); - const spheres = getStateObjectBoundingSpheres(cell?.obj); + const spheres = getStateObjectBoundingSpheres(plugin, cell); if (spheres) { out.push(...spheres); } else { @@ -76,14 +76,17 @@ function collectCellBoundingSpheres(out: Sphere3D[], plugin: PluginContext, cell } /** Return a set of bounding spheres of a plugin state object. Return `undefined` if this plugin state object type does not define bounding spheres. */ -function getStateObjectBoundingSpheres(obj: StateObject | undefined): Sphere3D[] | undefined { +function getStateObjectBoundingSpheres(plugin: PluginContext, cell: StateObjectCell | undefined): Sphere3D[] | undefined { + const obj = cell?.obj; if (!obj) return undefined; if (!obj.data) { console.warn('Focus: no data'); return undefined; } if (obj.data instanceof Structure) { - const sphere = Loci.getBoundingSphere(Structure.Loci(obj.data)); + const decorated = StateSelection.getDecorated(plugin.state.data, cell.transform.ref); + const data = decorated?.obj?.data ?? obj?.data; + const sphere = Loci.getBoundingSphere(Structure.Loci(data)); return sphere ? [sphere] : []; } else if (PluginStateObject.isRepresentation3D(obj)) { const out: Sphere3D[] = []; diff --git a/src/mol-plugin-state/manager/markdown-extensions.ts b/src/mol-plugin-state/manager/markdown-extensions.ts index a8944d9ec..1c8115457 100644 --- a/src/mol-plugin-state/manager/markdown-extensions.ts +++ b/src/mol-plugin-state/manager/markdown-extensions.ts @@ -6,7 +6,7 @@ import { getCellBoundingSphere } from '../../mol-plugin-state/manager/focus-camera/focus-object'; import { PluginStateObject } from '../../mol-plugin-state/objects'; -import { StateObjectCell } from '../../mol-state'; +import { StateObjectCell, StateSelection } from '../../mol-state'; import { PluginContext } from '../../mol-plugin/context'; import { Script } from '../../mol-script/script'; import { QueryContext, QueryFn, StructureElement, StructureSelection } from '../../mol-model/structure'; @@ -136,7 +136,8 @@ export const BuiltInMarkdownExtension: MarkdownExtension[] = [ if (!action.includes('focus')) { return; } - const spheres = structures.map(s => { + const decorated = structures.map(s => StateSelection.getDecorated(manager.plugin.state.data, s.transform.ref)); + const spheres = decorated.map(s => { if (!s.obj?.data) return undefined; const selection = query(new QueryContext(s.obj.data)); if (StructureSelection.isEmpty(selection)) return; diff --git a/src/mol-state/state/selection.ts b/src/mol-state/state/selection.ts index 1e8eb64be..884d17c49 100644 --- a/src/mol-state/state/selection.ts +++ b/src/mol-state/state/selection.ts @@ -375,6 +375,14 @@ namespace StateSelection { const first = children.first(); if (first && state.transforms.get(first).transformer.definition.isDecorator) return tryFindDecorator(state, first, transformer); } + + export function getDecorated(state: State, root: StateTransform.Ref): StateObjectCell { + const children = state.tree.children.get(root); + if (children.size !== 1) return state.cells.get(root) as any; + const first = children.first(); + if (first && state.transforms.get(first).transformer.definition.isDecorator) return getDecorated(state, first); + return state.cells.get(root) as any; + } } export { StateSelection }; \ No newline at end of file