molstar / molstar

A comprehensive macromolecular library
https://molstar.org
MIT License
589 stars 137 forks source link

custom focus behavior - hide ribbon representation #1112

Open Simard302 opened 2 weeks ago

Simard302 commented 2 weeks ago

Hi,

I am trying to modify the focus behavior to change some representations. One change is to make it so only residues in the surrounding area are visible (not just with clipping).

I created a custom behavior and am able to change most of the representations and create a new selection for external residues (making it target, surroundings, external). I am able to create a structure representation for this external selection, but the default ribbon representation also remains. Is there a way to remove it for just this selection?

dsehnal commented 2 weeks ago

How about adding a behavior that hides the default when something is focused? You can use setSubtreeVisibility for that.

Simard302 commented 2 weeks ago

Could I add the setSubtreeVisibility to the end of the focus and clear functions of my custom focus behavior? If so, do you know what I can pass into the function to make it work? I understand I need a StateTransform reference, and I was thinking of using StateTransforms.Model.StructureSelectionFromExpression and having an expression to get all the residues to hide. However, I am not getting the right type

dsehnal commented 2 weeks ago

Perhaps something along these lines:

const focusGroupRootRef: string = '';
const focusRepresentationRefs: Set<string> = new Set<string>();
const reprCells = plugin.state.data.selectQ(q => q
    .byRef(focusGroupRootRef)
    .ancestorOfType(PluginStateObject.Molecule.Structure)
    .subtree()
    .ofType(PluginStateObject.Molecule.Structure.Representation3D)
);
const toHide = reprCells.filter(c => !focusRepresentationRefs.has(c.transform.ref));
for (const cell of toHide) {
    setSubtreeVisibility(plugin.state.data, cell.transform.ref, false);
}

You can also put custom tags on your focus representations to make the toHide list easier.

Simard302 commented 1 week ago

So in this case, the focusRepresentationRefs would be the ref strings returned by ensureShape? What would be the focusGroupRootRef? Does it make sense to make the focusGroupRootRef be plugin.state.data.tree.root.ref and take off the .ancestorOfType(PluginStateObject.Molecule.Structure) when finding the reprCells since its already root.

Using this approach, reprCells has the focus refs in it, they then get filtered out to create the toHide. However, when I run the setSubtreeVisibility on the cells in toHide, it remains visible

dsehnal commented 1 week ago

So in this case, the focusRepresentationRefs would be the ref strings returned by ensureShape? What would be the focusGroupRootRef?

This is my mistake, I thought the focus representations were being added into a group, but they are not. I guess you can use TargetSel instead.

Does it make sense to make the focusGroupRootRef be plugin.state.data.tree.root.ref and take off the .ancestorOfType(PluginStateObject.Molecule.Structure) when finding the reprCells since its already root.

If you are ever only loading a single structure, than it would be the same and you don't have to do the other step.

Using this approach, reprCells has the focus refs in it, they then get filtered out to create the toHide. However, when I run the setSubtreeVisibility on the cells in toHide, it remains visible

I feel a little embarrassed about setSubtreeVisibility now, as the last param corresponds to isHidden state, so you actually need to pass true to hide things ... oops ... but live and learn.

Anyway, if you put this at the end of the focus function it will work:

const visible = new Set(Object.values(refs));
const reprs = this.plugin.state.data.selectQ(q => q.root.subtree().ofType(PluginStateObject.Molecule.Structure.Representation3D));
for (const cell of reprs) {
    if (visible.has(cell.transform.ref)) continue;
    setSubtreeVisibility(this.plugin.state.data, cell.transform.ref, true);
}

You will also need to show it again when the focus is cleared.

Simard302 commented 1 week ago

Thank you, that works great

Simard302 commented 1 week ago

Something I noticed, if I save the state as a file and then load it back, it reverts back to what it was before using setSubtreeVisibility. The same is the case when you hide and show components in the default viewer. Is it possible to make this part of the state without deleting the representation from the state?

~~EDIT: To add to this, it seems it is only because the information is not added to the .molj or .molx files. When I manually add:~~

"state": {
    "isHidden": true
 },

to one of the representations in the .molj file, and load it, that representation then becomes hidden

Ignore the above, all that needed to be done is to also add: await PluginCommands.State.Update(plugin, {state: plugin.state.data, tree: plugin.state.data.tree, options: {...}})