Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Representation params #41

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file, following t
- Breaking: Renamed geometrical primitive `line` to `tube`
- Breaking: Change multiple geometrical primitive parameters
- Multi-state data model tweaks
- Support for representation-specific parameters (customize presentation visuals)


## [v1.1.0] - 2024-12-09
Expand Down
15 changes: 15 additions & 0 deletions molviewspec/app/api/examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,21 @@ async def refs_example() -> MVSResponse:
return PlainTextResponse(builder.get_state())


@router.get("/repr-params")
async def repr_params_example() -> MVSResponse:
"""
Individual representations (cartoon, ball-and-stick, surface) can be further customized. The corresponding builder
function exposes additional method arguments depending on the chosen representation type.
"""
builder = create_builder()
component = (
builder.download(url=_url_for_mmcif("1a23")).parse(format="mmcif").model_structure(ref="structure").component()
)
component.representation(type="cartoon", size_factor=1.5, tubular_helices=True)
component.representation(type="surface", ignore_hydrogens=True).opacity(opacity=0.8)
return PlainTextResponse(builder.get_state())


@router.get("/primitives/cube")
async def primitives_cube_example() -> MVSResponse:
"""
Expand Down
82 changes: 72 additions & 10 deletions molviewspec/molviewspec/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import math
import os
from abc import ABC, abstractmethod
from typing import Literal, Self, Sequence
from typing import Any, Literal, Self, Sequence, overload

from pydantic import BaseModel, PrivateAttr

Expand Down Expand Up @@ -45,7 +45,7 @@
PrimitivesFromUriParams,
PrimitivesParams,
RefT,
RepresentationParams,
RepresentationTypeParams,
RepresentationTypeT,
SchemaFormatT,
SchemaT,
Expand Down Expand Up @@ -78,17 +78,14 @@ class _BuilderProtocol(ABC):

@property
@abstractmethod
def _root(self) -> Root:
...
def _root(self) -> Root: ...

@property
@abstractmethod
def _node(self) -> Node:
...
def _node(self) -> Node: ...

@abstractmethod
def _add_child(self, node: Node) -> None:
...
def _add_child(self, node: Node) -> None: ...


class _Base(BaseModel, _BuilderProtocol):
Expand Down Expand Up @@ -661,17 +658,82 @@ class Component(_Base, _FocusMixin):
Builder step with operations relevant for a particular component.
"""

@overload
def representation(
self,
*,
type: Literal["cartoon"],
size_factor: float = None,
tubular_helices: bool = None,
custom: CustomT = None,
ref: RefT = None,
) -> Representation:
"""
Add a cartoon representation for this component.
:param type: the type of this representation ('cartoon')
:param size_factor: adjust the scale of the visuals (relative to 1.0)
:param tubular_helices: simplify helices to tubes
:param custom: optional, custom data to attach to this node
:param ref: optional, reference that can be used to access this node
:return: a builder that handles operations at representation level
"""
...

@overload
def representation(
self,
*,
type: Literal["ball_and_stick"],
ignore_hydrogens: bool = None,
size_factor: float = None,
custom: CustomT = None,
ref: RefT = None,
) -> Representation:
"""
Add a ball-and-stick representation for this component.
:param type: the type of this representation ('ball_and_stick')
:param ignore_hydrogens: draw hydrogen atoms?
:param size_factor: adjust the scale of the visuals (relative to 1.0)
:param custom: optional, custom data to attach to this node
:param ref: optional, reference that can be used to access this node
:return: a builder that handles operations at representation level
"""
...

@overload
def representation(
self,
*,
type: Literal["surface"],
ignore_hydrogens: bool = None,
size_factor: float = None,
custom: CustomT = None,
ref: RefT = None,
) -> Representation:
"""
Add a surface representation for this component.
:param type: the type of this representation ('surface')
:param ignore_hydrogens: draw hydrogen atoms?
:param size_factor: adjust the scale of the visuals (relative to 1.0)
:param custom: optional, custom data to attach to this node
:param ref: optional, reference that can be used to access this node
:return: a builder that handles operations at representation level
"""
...

def representation(
self, *, type: RepresentationTypeT = "cartoon", custom: CustomT = None, ref: RefT = None
self, *, type: RepresentationTypeT = "cartoon", custom: CustomT = None, ref: RefT = None, **kwargs: Any
) -> Representation:
"""
Add a representation for this component.
:param type: the type of representation, defaults to 'cartoon'
:param custom: optional, custom data to attach to this node
:param ref: optional, reference that can be used to access this node
:param kwargs: optional, representation-specific params
:return: a builder that handles operations at representation level
"""
params = make_params(RepresentationParams, locals())
params_class = RepresentationTypeParams.get(type)
params = make_params(params_class, locals(), **kwargs)
node = Node(kind="representation", params=params)
self._add_child(node)
return Representation(node=node, root=self._root)
Expand Down
22 changes: 22 additions & 0 deletions molviewspec/molviewspec/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,28 @@ class RepresentationParams(BaseModel):
type: RepresentationTypeT = Field(description="Representation type, i.e. cartoon, ball-and-stick, etc.")


class CartoonParams(RepresentationParams):
type: Literal["cartoon"] = "cartoon"
size_factor: Optional[float] = Field(description="Scales the corresponding visuals.")
tubular_helices: Optional[bool] = Field(description="Simplify corkscrew helices to tubes.")
# TODO support for variable size, e.g. based on b-factors?


class BallAndStickParams(RepresentationParams):
type: Literal["ball_and_stick"] = "ball_and_stick"
ignore_hydrogens: Optional[bool] = Field(descripton="Controls whether hydrogen atoms are drawn.")
size_factor: Optional[float] = Field(description="Scales the corresponding visuals.")


class SurfaceParams(RepresentationParams):
type: Literal["surface"] = "surface"
ignore_hydrogens: Optional[bool] = Field(descripton="Controls whether hydrogen atoms are drawn.")
size_factor: Optional[float] = Field(description="Scales the corresponding visuals.")


RepresentationTypeParams = {t.__fields__["type"].default: t for t in (CartoonParams, BallAndStickParams, SurfaceParams)}


SchemaT = Literal[
"whole_structure",
"entity",
Expand Down
6 changes: 6 additions & 0 deletions molviewspec/molviewspec/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def make_params(params_type: Type[TParams], values=None, /, **more_values: objec
if values is None:
values = {}
result = {}
consumed_more_values = set()

# propagate custom properties
if values:
Expand All @@ -27,11 +28,16 @@ def make_params(params_type: Type[TParams], values=None, /, **more_values: objec

if more_values.get(key) is not None:
result[key] = more_values[key]
consumed_more_values.add(key)
elif values.get(key) is not None:
result[key] = values[key]
elif field.default is not None: # currently not used
result[key] = field.default

non_model_keys = set(more_values.keys()) - consumed_more_values
if non_model_keys:
raise ValueError(f"Encountered unknown attribute on {params_type}: {non_model_keys}")

return result # type: ignore


Expand Down