Skip to content
Merged
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 @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file, following t
## [Unreleased]

- add molviewspec-ts, a typescript implementation of mol-view-spec
- Add `putty` representation type with `size_theme` parameter (`"uniform"` for constant radius scaled by `size_factor`, `"uncertainty"` for B-factor/RMSF-driven radius)
- `color_from_uri` and `color_from_source` take `selector` parameter
- `label_from_*` and `tooltip_from_*` take `text_format` parameter
- `label_from_*` take `group_by_fields` parameter
Expand Down
18 changes: 17 additions & 1 deletion docs/docs/tree-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ Parent: `component` or `component_from_uri` or `component_from_source`

Params:

- **`type: `**`cartoon | backbone | ball_and_stick | line | spacefill | carbohydrate | surface`
- **`type: `**`cartoon | backbone | ball_and_stick | line | spacefill | carbohydrate | surface | putty`

Representation type

Expand Down Expand Up @@ -376,6 +376,22 @@ Params:

Default: `1`

**Case `type: "putty"`:**

- **`size_factor?: `**`number`

Scales the corresponding visuals.

Default: `1`

- **`size_theme?: `**`"uniform" | "uncertainty"`

Controls how the tube radius is determined.
`"uniform"` uses a constant radius scaled by `size_factor`.
`"uncertainty"` drives the radius from per-residue B-factor/RMSF values.

Default: `"uniform"`

**Case `type: "surface"`:**

- **`surface_type?: `**`"molecular" | "gaussian"`
Expand Down
18 changes: 18 additions & 0 deletions molviewspec-ts/molviewspec/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import type {
PrimitiveLabelParams,
PrimitivesFromUriParams,
PrimitivesParams,
PuttyParams,
RepresentationParams,
Snapshot,
SnapshotMetadata,
Expand Down Expand Up @@ -1112,6 +1113,23 @@ export class Component extends Base {
* })
* ```
*
* @example Putty with constant (uniform) tube radius
* ```typescript
* component.representation({
* type: "putty",
* size_theme: "uniform",
* size_factor: 0.5
* })
* ```
*
* @example Putty with B-factor/RMSF-driven tube radius
* ```typescript
* component.representation({
* type: "putty",
* size_theme: "uncertainty"
* })
* ```
*
* @example Cartoon with custom data
* ```typescript
* component.representation({
Expand Down
13 changes: 13 additions & 0 deletions molviewspec-ts/molviewspec/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import type {
SchemaT,
StateTreeT,
StructureTypeT,
PuttySizeThemeT,
SurfaceTypeT,
Vec3,
VolumeRepresentationTypeT,
Expand Down Expand Up @@ -286,6 +287,17 @@ export interface CarbohydrateParams {
[key: string]: unknown;
}

/**
* Putty parameters.
* The tube radius can be driven by a constant size_factor ("uniform") or
* per-residue B-factor/RMSF values ("uncertainty").
*/
export interface PuttyParams {
size_factor?: number;
size_theme?: PuttySizeThemeT;
[key: string]: unknown;
}

/**
* Surface parameters.
*/
Expand Down Expand Up @@ -763,6 +775,7 @@ export type NodeParams =
| LineRepresentationParams
| SpacefillParams
| CarbohydrateParams
| PuttyParams
| SurfaceParams
| VolumeRepresentationParams
| VolumeIsoSurfaceParams
Expand Down
10 changes: 9 additions & 1 deletion molviewspec-ts/molviewspec/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,15 @@ export type RepresentationTypeT =
| "isosurface"
| "carbohydrate"
| "backbone"
| "line";
| "line"
| "putty";

/**
* Size theme for putty representation.
* - "uniform": constant tube radius, scaled by size_factor (default)
* - "uncertainty": per-residue radius driven by B-factor/RMSF values
*/
export type PuttySizeThemeT = "uniform" | "uncertainty";

export type VolumeRepresentationTypeT = "isosurface" | "grid_slice";

Expand Down
99 changes: 99 additions & 0 deletions molviewspec-ts/tests/serialization_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,105 @@ Deno.test("serialization - multi-state snapshots", () => {
assertEquals(states.snapshots[1].metadata.transition_duration_ms, 500);
});

Deno.test("serialization - putty representation default", () => {
const builder = createBuilder();
builder
.download({ url: "https://files.wwpdb.org/download/1cbs.cif" })
.parse({ format: "mmcif" })
.modelStructure()
.component({ selector: "polymer" })
.representation({ type: "putty" });

const state = builder.getState();
const stateJson = JSON.stringify(state);

assertEquals(stateJson.includes("putty"), true);
assertEquals(stateJson.includes("representation"), true);
});

Deno.test("serialization - putty representation uniform size theme", () => {
const builder = createBuilder();
builder
.download({ url: "https://files.wwpdb.org/download/1cbs.cif" })
.parse({ format: "mmcif" })
.modelStructure()
.component({ selector: "polymer" })
.representation({ type: "putty", size_theme: "uniform", size_factor: 0.5 });

const state = builder.getState();
const stateJson = JSON.stringify(state);

assertEquals(stateJson.includes("putty"), true);
assertEquals(stateJson.includes("uniform"), true);
assertEquals(stateJson.includes("0.5"), true);
});

Deno.test("serialization - putty representation uncertainty size theme", () => {
const builder = createBuilder();
builder
.download({ url: "https://files.wwpdb.org/download/1cbs.cif" })
.parse({ format: "mmcif" })
.modelStructure()
.component({ selector: "polymer" })
.representation({ type: "putty", size_theme: "uncertainty" });

const state = builder.getState();
const stateJson = JSON.stringify(state);

assertEquals(stateJson.includes("putty"), true);
assertEquals(stateJson.includes("uncertainty"), true);
});

Deno.test("serialization - putty uncertainty with size_factor", () => {
const builder = createBuilder();
builder
.download({ url: "https://files.wwpdb.org/download/1cbs.cif" })
.parse({ format: "mmcif" })
.modelStructure()
.component({ selector: "polymer" })
.representation({
type: "putty",
size_theme: "uncertainty",
size_factor: 2.0,
});

const state = builder.getState();
const stateJson = JSON.stringify(state);

assertEquals(stateJson.includes("putty"), true);
assertEquals(stateJson.includes("uncertainty"), true);
assertEquals(stateJson.includes("2"), true);
});

Deno.test("serialization - putty node structure", () => {
const builder = createBuilder();
builder
.download({ url: "https://files.wwpdb.org/download/1cbs.cif" })
.parse({ format: "mmcif" })
.modelStructure()
.component({ selector: "polymer" })
.representation({ type: "putty", size_theme: "uniform", size_factor: 1.5 });

const state = builder.getState();

// Walk to the representation node
assertExists(state.root.children);
const download = state.root.children[0];
assertExists(download.children);
const parse = download.children[0];
assertExists(parse.children);
const structure = parse.children[0];
assertExists(structure.children);
const component = structure.children[0];
assertExists(component.children);
const representation = component.children[0];

assertEquals(representation.kind, "representation");
assertEquals((representation.params as any)?.type, "putty");
assertEquals((representation.params as any)?.size_theme, "uniform");
assertEquals((representation.params as any)?.size_factor, 1.5);
});

Deno.test("serialization - canvas and camera", () => {
const builder = createBuilder();
builder.canvas({ background_color: "#ffffff" });
Expand Down
22 changes: 12 additions & 10 deletions molviewspec/app/api/examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,15 +558,19 @@ async def refs_example() -> MVSResponse:
@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.
Individual representations (cartoon, ball-and-stick, surface, putty) can be further customized. The corresponding
builder function exposes additional method arguments depending on the chosen representation type.
Putty supports two size themes: 'uniform' (constant radius scaled by size_factor) and
'uncertainty' (per-residue radius driven by B-factor/RMSF values).
"""
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)
component.representation(type="putty", size_theme="uniform", size_factor=0.5)
component.representation(type="putty", size_theme="uncertainty")
return JSONResponse(builder.get_state().to_dict())


Expand Down Expand Up @@ -2950,14 +2954,14 @@ async def portfolio_modres() -> MVSResponse:
async def portfolio_bfactor() -> MVSResponse:
"""
Entry structure colored by B-factor, as created by PDBImages.
(We are missing putty representation and size theme!)
Uses putty representation with size_theme='uncertainty' so the tube radius also encodes B-factor/RMSF.
"""
ID = "1tqn"
builder = create_builder()
structure_url = _url_for_mmcif(ID)
annotation_url = f"http://0.0.0.0:9000/api/v1/examples/data/file/{ID}/bfactor.cif"
struct = builder.download(url=structure_url).parse(format="mmcif").model_structure()
struct.component(selector="polymer").representation(type="cartoon").color_from_uri(
struct.component(selector="polymer").representation(type="putty", size_theme="uncertainty").color_from_uri(
uri=annotation_url, format="cif", schema="all_atomic", category_name=f"bfactor"
)
struct.component(selector="ligand").representation(type="ball_and_stick").color_from_uri(
Expand Down Expand Up @@ -3068,7 +3072,6 @@ async def portfolio_pdbekb_segment_superpose(
) -> MVSResponse:
"""
"PDBe-KB segment superpose view" from https://docs.google.com/spreadsheets/d/1sUSWmBLfKMmPLW2yqVnxWQTQoVk6SmQppdCfItyO1m0/edit#gid=0
(We are missing putty representation!)
"""
builder = create_builder()
structure_url1 = _url_for_mmcif(id1) # TODO use model server, only retrieve the chain
Expand Down Expand Up @@ -3102,7 +3105,7 @@ async def portfolio_pdbekb_segment_superpose(
)
.component(selector=ComponentExpression(label_asym_id=chain2))
.tooltip(text=f"{id2}, chain {chain2}")
.representation(type="cartoon") # should be putty
.representation(type="putty", size_theme="uniform")
.color(color="#cc5a03")
)
builder.canvas(background_color="#ffffff")
Expand All @@ -3113,7 +3116,6 @@ async def portfolio_pdbekb_segment_superpose(
async def portfolio_pdbekb_ligand_superpose(chains: str = "1tqn:A,2nnj:A") -> MVSResponse:
"""
"PDBe-KB ligand superpose view" from https://docs.google.com/spreadsheets/d/1sUSWmBLfKMmPLW2yqVnxWQTQoVk6SmQppdCfItyO1m0/edit#gid=0
(We are missing putty representation!)
"""
builder = create_builder()
for i, id_chain in enumerate(chains.split(",")):
Expand Down Expand Up @@ -3141,9 +3143,9 @@ async def portfolio_pdbekb_ligand_superpose(chains: str = "1tqn:A,2nnj:A") -> MV
),
)
if i == 0:
struct.component(selector=ComponentExpression(label_asym_id=chain)).representation(type="cartoon").color(
color="#1d9873"
) # should be putty
struct.component(selector=ComponentExpression(label_asym_id=chain)).representation(
type="putty", size_theme="uniform"
).color(color="#1d9873")
struct.component(selector="ligand").representation(type="ball_and_stick").color(color="#f602f7")
struct.component(selector="ion").representation(type="ball_and_stick").color(color="#f602f7")
builder.canvas(background_color="#ffffff")
Expand Down
25 changes: 25 additions & 0 deletions molviewspec/molviewspec/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
PrimitivesFromUriParams,
PrimitivesParams,
RefT,
PuttyParams,
PuttySizeThemeT,
RepresentationTypeParams,
RepresentationTypeT,
SchemaFormatT,
Expand Down Expand Up @@ -1146,6 +1148,29 @@ def representation(
"""
...

@overload
def representation(
self,
*,
type: Literal["putty"],
size_factor: float | None = None,
size_theme: PuttySizeThemeT | None = None,
custom: CustomT = None,
ref: RefT = None,
) -> Representation:
"""
Add a putty representation for this component.
:param type: the type of this representation ('putty')
:param size_factor: adjust the scale of the visuals (relative to 1.0)
:param size_theme: controls how the tube radius is determined.
'uniform' uses a constant radius scaled by size_factor (default).
'uncertainty' drives the radius from per-residue B-factor/RMSF values.
: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,
Expand Down
20 changes: 19 additions & 1 deletion molviewspec/molviewspec/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ class ComponentExpression(BaseModel):
)


RepresentationTypeT = Literal["ball_and_stick", "spacefill", "cartoon", "surface", "isosurface", "carbohydrate"]
RepresentationTypeT = Literal["ball_and_stick", "spacefill", "cartoon", "surface", "isosurface", "carbohydrate", "putty"]
VolumeRepresentationTypeT = Literal["isosurface", "grid_slice"]
ColorNamesT = Literal[
"aliceblue",
Expand Down Expand Up @@ -1048,6 +1048,23 @@ class CarbohydrateParams(RepresentationParams):
size_factor: Optional[float] = Field(None, description="Scales the corresponding visuals.")


PuttySizeThemeT = Literal["uniform", "uncertainty"]


class PuttyParams(RepresentationParams):
type: Literal["putty"] = "putty"
size_factor: Optional[float] = Field(None, description="Scales the corresponding visuals.")
size_theme: Optional[PuttySizeThemeT] = Field(
None,
description=(
"Controls how the tube radius is determined. "
"'uniform' uses a constant radius scaled by size_factor. "
"'uncertainty' drives the radius from per-residue B-factor/RMSF values. "
"Default: 'uniform'."
),
)


SurfaceTypeT = Literal["molecular", "gaussian"]


Expand All @@ -1070,6 +1087,7 @@ class SurfaceParams(RepresentationParams):
LineRepresenatationParams,
SpacefillParams,
CarbohydrateParams,
PuttyParams,
SurfaceParams,
)
}
Expand Down
Loading