From eed2ed687b9316307c615424ea6c22a8617971df Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Thu, 31 Oct 2024 09:53:19 +0100 Subject: [PATCH 01/24] data models for additional primitives --- molviewspec/molviewspec/nodes.py | 89 ++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 11 deletions(-) diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index e41aab9..c1c9a77 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -44,6 +44,17 @@ CustomT = Optional[Mapping[str, Any]] RefT = Optional[str] +# TODO: to be used for angle, dihedral +class LabelCommonProps(BaseModel): + label_template: Optional[str] = Field( + description="Template used to construct the label. Use {{distance}} as placeholder for the distance." + ) + label_size: Optional[float | Literal["auto"]] = Field( + description="Size of the label. Auto scales it by the distance." + ) + label_auto_size_scale: Optional[float] = Field(description="Scaling factor for auto size.") + label_auto_size_min: Optional[float] = Field(description="Minimum size for auto size.") + label_color: Optional[ColorT] = Field(description="Color of the label.") class Node(BaseModel): """ @@ -725,18 +736,8 @@ class LineParams(_LineParamsBase): tooltip: Optional[str] = Field(description="Tooltip to show when hovering on the line.") -class DistanceMeasurementParams(_LineParamsBase): +class DistanceMeasurementParams(_LineParamsBase, LabelCommonProps): kind: Literal["distance_measurement"] = "distance_measurement" - label_template: Optional[str] = Field( - description="Template used to construct the label. Use {{distance}} as placeholder for the distance." - ) - label_size: Optional[float | Literal["auto"]] = Field( - description="Size of the label. Auto scales it by the distance." - ) - label_auto_size_scale: Optional[float] = Field(description="Scaling factor for auto size.") - label_auto_size_min: Optional[float] = Field(description="Minimum size for auto size.") - label_color: Optional[ColorT] = Field(description="Color of the label.") - class PrimitiveLabelParams(_LineParamsBase): kind: Literal["label"] = "label" @@ -748,6 +749,7 @@ class PrimitiveLabelParams(_LineParamsBase): class PlaneParams(BaseModel): + # TODO: bounding_box? kind: Literal["plane"] = "plane" point: PrimitivePositionT = Field(description="Point on plane.") normal: Vec3[float] = Field(description="Normal vector of plane.") @@ -769,3 +771,68 @@ def validate_state_tree(json: str) -> None: :raises ValidationError if JSON is malformed or state tree type definitions are violated """ State.parse_raw(json) + +# TODO: to be discussed +# class TooltipAndColorProps(BaseModel): +# tooltip: Optional[str] = Field(description="Default tooltip for primitives in this group") +# color: Optional[ColorT] = Field( +# description="Color of the line. If not specified, the primitives group color is used." +# ) + +# TODO: fields instead of plain types +class CircleParams(BaseModel): + center: Vec3 = Field(description="The center of the circle.") + # TODO: elaborate names and semantics depending on + # how Mol* implements circles + # (should be dir_major and dir_minor perhaps as these two here are just Vec3) + major_axis: PrimitivePositionT = Field(description="Major axis of this circle.") + minor_axis: PrimitivePositionT = Field(description="Minor axis of this circle.") + +# TODO: add collection of descriptions for the fields with the same name +class Polygon(BaseModel): + vertices: list[float] = Field(description="3N length array of floats with vertex position (x1, y1, z1, ...)") + +class Star(BaseModel): + center: Vec3 = Field(description="The center of the star.") + inner_radius: float = Field(description="The inner radius of the star") + outer_radius: float = Field(description="The outer radius of the star") + # TODO: is this correct meaning? + point_count: int = Field(description="The number of points the star contains") + # TODO: inherit from TransformParams instead? + rotation: Optional[Mat3[float]] = Field( + description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", + ) + +class Box(TransformParams): + center: Vec3 = Field(description="The center of the box") + # TODO: is this correct meaning? + extent: Vec3 = Field(description="The height and width of the box") + # TODO: include in TransformParams instead? + scaling: Optional[Vec3[float]] = Field(description="3d vector describing the scaling") + as_edges: Optional[bool] = Field(description="Determine whether to render the box as edges") + # TODO: meaning of this? Thickness of edges? + edge_radius: Optional[float] = Field(description="The thickness of edges.") + +class Cylinder(BaseModel): + center: Vec3 = Field(description="The center of the box") + radius_top: float = Field(description="The radius of the top of the cylinder top. Radius equal to zero will yield a cone.") + radius_bottom: float = Field(description="The radius of the bottom of the cylinder. Radius equal to zero will yield a reversed cone.") + height: float = Field(description="The height of the cone.") + # TODO: meaning of the following two? Check Sebastian's answers in some of the PRs. + theta_start: float = Field(description="TODO") + theta_length: float = Field(description="TODO") + # TODO: type for rotation as field + rotation: Optional[Mat3[float]] = Field( + description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", + ) + bottom_cap: bool = Field(description="Determine whether to cap the top of the cylinder.") + top_cap: bool = Field(description="Determine whether to cap the bottom of the cylinder.") + +# class Arrow(BaseModel): +# # TODO: better name, "from" is a reserved keyword +# line_from: Vec3 = Field(description="The center of the box") +# line_to: +# cylinder_radius: +# arrow_radius: +# arrow_height: +# arrow_from, arrow_to, From 4fad172e57dbd163c3388e054d39f9023fdea049 Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Thu, 31 Oct 2024 13:09:57 +0100 Subject: [PATCH 02/24] more models --- molviewspec/molviewspec/nodes.py | 64 ++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index c1c9a77..6a54096 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -615,7 +615,6 @@ class TransformParams(BaseModel): ) translation: Optional[Vec3[float]] = Field(description="3d vector describing the translation") - class CameraParams(BaseModel): """ Controls the global camera position. @@ -808,8 +807,8 @@ class Box(TransformParams): # TODO: is this correct meaning? extent: Vec3 = Field(description="The height and width of the box") # TODO: include in TransformParams instead? - scaling: Optional[Vec3[float]] = Field(description="3d vector describing the scaling") - as_edges: Optional[bool] = Field(description="Determine whether to render the box as edges") + scaling: Optional[Vec3[float]] = Field(description="3d vector describing the scaling.") + as_edges: Optional[bool] = Field(description="Determine whether to render the box as edges.") # TODO: meaning of this? Thickness of edges? edge_radius: Optional[float] = Field(description="The thickness of edges.") @@ -828,11 +827,54 @@ class Cylinder(BaseModel): bottom_cap: bool = Field(description="Determine whether to cap the top of the cylinder.") top_cap: bool = Field(description="Determine whether to cap the bottom of the cylinder.") -# class Arrow(BaseModel): -# # TODO: better name, "from" is a reserved keyword -# line_from: Vec3 = Field(description="The center of the box") -# line_to: -# cylinder_radius: -# arrow_radius: -# arrow_height: -# arrow_from, arrow_to, + +# NOTE: arrow should be derived from line perhaps? +# This allows us to make use of inheritance +class Arrow(_LineParamsBase): + # TODO: clarify meaning of the following to and modify depending + # on frontend implementation + arrow_radius: float = Field(description="The radius (extent) of the arrow.") + arrow_height: float = Field(description="The height of the arrow.") + arrow_from: Vec3 = Field(description="Start of the arrow.") + arrow_to: Vec3 = Field(description="End of the arrow.") + +SolidKindTypeT = Literal["tetra", "octa", "dodeca", "icosahedron"] + +class PlatonicSolid(BaseModel): + solid_kind: SolidKindTypeT + center: float = Field(description="The center of the platonic solid.") + radius: float = Field(description="The radius of the platonic solid.") + rotation: Optional[Mat3[float]] = Field( + description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", + ) + +class Prism(BaseModel): + position: PrimitivePositionT = Field(description="Position of this prism.") + # TODO: meaning? + base_point_count: int = Field(description="Count of base points") + height: float = Field(description="The height of the prism.") + rotation: Optional[Mat3[float]] = Field( + description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", + ) + +class Pyramid(BaseModel): + vertices: list[float] = Field(description="3N length array of floats with vertex position (x1, y1, z1, ...)") + translation: Optional[Vec3[float]] = Field(description="3d vector describing the translation") + scaling: Optional[Vec3[float]] = Field(description="3d vector describing the scaling") + rotation: Optional[Mat3[float]] = Field( + description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", + ) + +class Sphere(BaseModel): + center: Vec3 = Field(description="The center of the circle.") + radius: float | PrimitivePositionT = Field(description="The radius of the sphere.") + + # TODO: +# class Torus(BaseModel): center, outer_radius, tube_radius, theta_start, theta_length, rotation +# Wedge: center, width, height, depth, rotation +# Ellipsoid: direction_major, direction_minor, direction_normal, center, radius_scale + +# TODO: +# Distance: a: PositionT, b, label props (template, color, …), line props +# Angle:a, b, c, label props (template, color, …), line props, angle visual props +# Dihedral: a, b, c, d, label props (template, color, …), line props, angle visual props From 73a41d599b1bb14b6c811f86cee8c6319f5b71f8 Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Thu, 31 Oct 2024 13:26:45 +0100 Subject: [PATCH 03/24] more models --- molviewspec/molviewspec/nodes.py | 36 ++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index 6a54096..aef4bd3 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -870,11 +870,39 @@ class Sphere(BaseModel): radius: float | PrimitivePositionT = Field(description="The radius of the sphere.") # TODO: -# class Torus(BaseModel): center, outer_radius, tube_radius, theta_start, theta_length, rotation -# Wedge: center, width, height, depth, rotation -# Ellipsoid: direction_major, direction_minor, direction_normal, center, radius_scale +class Torus(BaseModel): + center: Vec3 = Field(description="The center of the torus.") + outer_radius: float = Field(description="The outer radius of the torus") + tube_radius: float = Field(description="The tube radius.") + # TODO: + theta_start: float = Field(description="TODO") + # TODO: + theta_length: float = Field(description="TODO") + rotation: Optional[Mat3[float]] = Field( + description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", + ) + +class Wedge(BaseModel): + center: Vec3 = Field(description="The center of the wedge.") + width: float = Field(description="The width of the wedge.") + height: float = Field(description="The height of the wedge.") + depth: float = Field(description="The depth of the wedge.") + rotation: Optional[Mat3[float]] = Field( + description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", + ) + +class Ellipsoid(BaseModel): + # TODO: adjust based on frontend implementation + direction_major: Vec3[float] = Field(description="Major direction of the ellipsoid.") + direction_minor: Vec3[float] = Field(description="Minor direction of the ellipsoid.") + direction_normal: Vec3[float] = Field(description="Normal direction of the ellipsoid.") + center: Vec3 = Field(description="The center of the ellipsoid.") + # TODO: is the meaning correct + radius_scale: Optional[Vec3[float]] = Field(description="3d vector describing the radius scaling.") # TODO: -# Distance: a: PositionT, b, label props (template, color, …), line props +class Distance(PrimitiveLabelParams): + a: PrimitivePositionT = Field(description="The first point.") + b: PrimitivePositionT = Field(description="The second point.") # Angle:a, b, c, label props (template, color, …), line props, angle visual props # Dihedral: a, b, c, d, label props (template, color, …), line props, angle visual props From 22c43244fee5c2e44936d2e371c2d452ce5fe149 Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Fri, 1 Nov 2024 08:57:36 +0100 Subject: [PATCH 04/24] naming fixes --- molviewspec/molviewspec/nodes.py | 36 ++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index aef4bd3..c1c6241 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -802,7 +802,7 @@ class Star(BaseModel): description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", ) -class Box(TransformParams): +class BoxParams(TransformParams): center: Vec3 = Field(description="The center of the box") # TODO: is this correct meaning? extent: Vec3 = Field(description="The height and width of the box") @@ -812,7 +812,7 @@ class Box(TransformParams): # TODO: meaning of this? Thickness of edges? edge_radius: Optional[float] = Field(description="The thickness of edges.") -class Cylinder(BaseModel): +class CylinderParams(BaseModel): center: Vec3 = Field(description="The center of the box") radius_top: float = Field(description="The radius of the top of the cylinder top. Radius equal to zero will yield a cone.") radius_bottom: float = Field(description="The radius of the bottom of the cylinder. Radius equal to zero will yield a reversed cone.") @@ -830,7 +830,7 @@ class Cylinder(BaseModel): # NOTE: arrow should be derived from line perhaps? # This allows us to make use of inheritance -class Arrow(_LineParamsBase): +class ArrowParams(_LineParamsBase): # TODO: clarify meaning of the following to and modify depending # on frontend implementation arrow_radius: float = Field(description="The radius (extent) of the arrow.") @@ -840,7 +840,7 @@ class Arrow(_LineParamsBase): SolidKindTypeT = Literal["tetra", "octa", "dodeca", "icosahedron"] -class PlatonicSolid(BaseModel): +class PlatonicSolidParams(BaseModel): solid_kind: SolidKindTypeT center: float = Field(description="The center of the platonic solid.") radius: float = Field(description="The radius of the platonic solid.") @@ -848,7 +848,7 @@ class PlatonicSolid(BaseModel): description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", ) -class Prism(BaseModel): +class PrismParams(BaseModel): position: PrimitivePositionT = Field(description="Position of this prism.") # TODO: meaning? base_point_count: int = Field(description="Count of base points") @@ -857,7 +857,7 @@ class Prism(BaseModel): description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", ) -class Pyramid(BaseModel): +class PyramidParams(BaseModel): vertices: list[float] = Field(description="3N length array of floats with vertex position (x1, y1, z1, ...)") translation: Optional[Vec3[float]] = Field(description="3d vector describing the translation") scaling: Optional[Vec3[float]] = Field(description="3d vector describing the scaling") @@ -865,12 +865,12 @@ class Pyramid(BaseModel): description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", ) -class Sphere(BaseModel): +class SphereParams(BaseModel): center: Vec3 = Field(description="The center of the circle.") radius: float | PrimitivePositionT = Field(description="The radius of the sphere.") # TODO: -class Torus(BaseModel): +class TorusParams(BaseModel): center: Vec3 = Field(description="The center of the torus.") outer_radius: float = Field(description="The outer radius of the torus") tube_radius: float = Field(description="The tube radius.") @@ -882,7 +882,7 @@ class Torus(BaseModel): description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", ) -class Wedge(BaseModel): +class WedgeParams(BaseModel): center: Vec3 = Field(description="The center of the wedge.") width: float = Field(description="The width of the wedge.") height: float = Field(description="The height of the wedge.") @@ -891,7 +891,7 @@ class Wedge(BaseModel): description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", ) -class Ellipsoid(BaseModel): +class EllipsoidParams(BaseModel): # TODO: adjust based on frontend implementation direction_major: Vec3[float] = Field(description="Major direction of the ellipsoid.") direction_minor: Vec3[float] = Field(description="Minor direction of the ellipsoid.") @@ -901,8 +901,18 @@ class Ellipsoid(BaseModel): radius_scale: Optional[Vec3[float]] = Field(description="3d vector describing the radius scaling.") # TODO: -class Distance(PrimitiveLabelParams): +class DistanceParams(LabelCommonProps): a: PrimitivePositionT = Field(description="The first point.") b: PrimitivePositionT = Field(description="The second point.") -# Angle:a, b, c, label props (template, color, …), line props, angle visual props -# Dihedral: a, b, c, d, label props (template, color, …), line props, angle visual props + +class AngleParams(LabelCommonProps): + a: PrimitivePositionT = Field(description="The first point.") + b: PrimitivePositionT = Field(description="The second point.") + c: PrimitivePositionT = Field(description="The third point.") + +class DihedralParams(LabelCommonProps, _LineParamsBase): + a: PrimitivePositionT = Field(description="The first point.") + b: PrimitivePositionT = Field(description="The second point.") + c: PrimitivePositionT = Field(description="The third point.") + d: PrimitivePositionT = Field(description="The fourth point.") + # TODO: angle visual props - what does it mean? From b4af2facb1f59a81c38f0ac244e508a5891669d7 Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Fri, 1 Nov 2024 09:02:12 +0100 Subject: [PATCH 05/24] types --- molviewspec/molviewspec/nodes.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index c1c6241..02ce6b4 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -771,15 +771,9 @@ def validate_state_tree(json: str) -> None: """ State.parse_raw(json) -# TODO: to be discussed -# class TooltipAndColorProps(BaseModel): -# tooltip: Optional[str] = Field(description="Default tooltip for primitives in this group") -# color: Optional[ColorT] = Field( -# description="Color of the line. If not specified, the primitives group color is used." -# ) - # TODO: fields instead of plain types class CircleParams(BaseModel): + kind: Literal["circle"] = "circle" center: Vec3 = Field(description="The center of the circle.") # TODO: elaborate names and semantics depending on # how Mol* implements circles @@ -789,9 +783,11 @@ class CircleParams(BaseModel): # TODO: add collection of descriptions for the fields with the same name class Polygon(BaseModel): + kind: Literal["polygon"] = "polygon" vertices: list[float] = Field(description="3N length array of floats with vertex position (x1, y1, z1, ...)") class Star(BaseModel): + kind: Literal["star"] = "star" center: Vec3 = Field(description="The center of the star.") inner_radius: float = Field(description="The inner radius of the star") outer_radius: float = Field(description="The outer radius of the star") @@ -803,6 +799,7 @@ class Star(BaseModel): ) class BoxParams(TransformParams): + kind: Literal["box"] = "box" center: Vec3 = Field(description="The center of the box") # TODO: is this correct meaning? extent: Vec3 = Field(description="The height and width of the box") @@ -813,6 +810,7 @@ class BoxParams(TransformParams): edge_radius: Optional[float] = Field(description="The thickness of edges.") class CylinderParams(BaseModel): + kind: Literal["cylinder"] = "cylinder" center: Vec3 = Field(description="The center of the box") radius_top: float = Field(description="The radius of the top of the cylinder top. Radius equal to zero will yield a cone.") radius_bottom: float = Field(description="The radius of the bottom of the cylinder. Radius equal to zero will yield a reversed cone.") @@ -833,6 +831,7 @@ class CylinderParams(BaseModel): class ArrowParams(_LineParamsBase): # TODO: clarify meaning of the following to and modify depending # on frontend implementation + kind: Literal["arrow"] = "arrow" arrow_radius: float = Field(description="The radius (extent) of the arrow.") arrow_height: float = Field(description="The height of the arrow.") arrow_from: Vec3 = Field(description="Start of the arrow.") @@ -841,6 +840,7 @@ class ArrowParams(_LineParamsBase): SolidKindTypeT = Literal["tetra", "octa", "dodeca", "icosahedron"] class PlatonicSolidParams(BaseModel): + kind: Literal["platonic_solid"] = "platonic_solid" solid_kind: SolidKindTypeT center: float = Field(description="The center of the platonic solid.") radius: float = Field(description="The radius of the platonic solid.") @@ -849,6 +849,7 @@ class PlatonicSolidParams(BaseModel): ) class PrismParams(BaseModel): + kind: Literal["prism"] = "prism" position: PrimitivePositionT = Field(description="Position of this prism.") # TODO: meaning? base_point_count: int = Field(description="Count of base points") @@ -858,6 +859,7 @@ class PrismParams(BaseModel): ) class PyramidParams(BaseModel): + kind: Literal["pyramid"] = "pyramid" vertices: list[float] = Field(description="3N length array of floats with vertex position (x1, y1, z1, ...)") translation: Optional[Vec3[float]] = Field(description="3d vector describing the translation") scaling: Optional[Vec3[float]] = Field(description="3d vector describing the scaling") @@ -866,11 +868,13 @@ class PyramidParams(BaseModel): ) class SphereParams(BaseModel): + kind: Literal["sphere"] = "sphere" center: Vec3 = Field(description="The center of the circle.") radius: float | PrimitivePositionT = Field(description="The radius of the sphere.") # TODO: class TorusParams(BaseModel): + kind: Literal["torus"] = "torus" center: Vec3 = Field(description="The center of the torus.") outer_radius: float = Field(description="The outer radius of the torus") tube_radius: float = Field(description="The tube radius.") @@ -883,6 +887,7 @@ class TorusParams(BaseModel): ) class WedgeParams(BaseModel): + kind: Literal["wedge"] = "wedge" center: Vec3 = Field(description="The center of the wedge.") width: float = Field(description="The width of the wedge.") height: float = Field(description="The height of the wedge.") @@ -893,6 +898,7 @@ class WedgeParams(BaseModel): class EllipsoidParams(BaseModel): # TODO: adjust based on frontend implementation + kind: Literal["ellipsoid"] = "ellipsoid" direction_major: Vec3[float] = Field(description="Major direction of the ellipsoid.") direction_minor: Vec3[float] = Field(description="Minor direction of the ellipsoid.") direction_normal: Vec3[float] = Field(description="Normal direction of the ellipsoid.") @@ -902,15 +908,18 @@ class EllipsoidParams(BaseModel): # TODO: class DistanceParams(LabelCommonProps): + kind: Literal["disatance"] = "disatance" a: PrimitivePositionT = Field(description="The first point.") b: PrimitivePositionT = Field(description="The second point.") class AngleParams(LabelCommonProps): + kind: Literal["angle"] = "angle" a: PrimitivePositionT = Field(description="The first point.") b: PrimitivePositionT = Field(description="The second point.") c: PrimitivePositionT = Field(description="The third point.") class DihedralParams(LabelCommonProps, _LineParamsBase): + kind: Literal["dihedral"] = "dihedral" a: PrimitivePositionT = Field(description="The first point.") b: PrimitivePositionT = Field(description="The second point.") c: PrimitivePositionT = Field(description="The third point.") From 5605c4e878cc569bbdb0f562a063c5ce9537ab53 Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Fri, 1 Nov 2024 09:03:47 +0100 Subject: [PATCH 06/24] box --- molviewspec/molviewspec/builder.py | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/molviewspec/molviewspec/builder.py b/molviewspec/molviewspec/builder.py index 1796410..43e3347 100644 --- a/molviewspec/molviewspec/builder.py +++ b/molviewspec/molviewspec/builder.py @@ -12,6 +12,7 @@ from pydantic import BaseModel, PrivateAttr from molviewspec.nodes import ( + BoxParams, CameraParams, CanvasParams, ColorFromSourceParams, @@ -566,6 +567,7 @@ def tooltip_from_source( def transform( self, *, + # TODO: given as Optional[Mat3[float]] in nodes.py rotation: Sequence[float] | None = None, translation: Sequence[float] | None = None, custom: CustomT = None, @@ -935,3 +937,33 @@ def label( node = Node(kind="primitive", params=params) self._add_child(node) return self + + def box(self, + *, + center: Vec3, + extent: Vec3, + # TODO: adjust default? + scaling: Vec3[float] | None = 1.0, + as_edges: bool | None = False, + edge_radius: float | None = None, + rotation: Sequence[float] | None = None, + translation: Vec3[float] | None = None, + custom: CustomT = None, + ref: RefT = None, + ) -> Primitives: + # TODO annotation + # """ + # Defines a box + # :param center: center of the box + # :param extent: height and width of the box + # :param scaling: scaling + # :param as_edges: 3d vector describing the scaling. + # :param label_offset: camera-facing offset to prevent overlap with geometry + # :param custom: optional, custom data to attach to this node + # :param ref: optional, reference that can be used to access this node + # :return: this builder + # """ + params = make_params(BoxParams, {"kind": "box", **locals()}) + node = Node(kind="primitive", params=params) + self._add_child(node) + return self \ No newline at end of file From 72f2c2c9d5fb42672ddf5fa320c8fadecf09a6ff Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Fri, 1 Nov 2024 11:12:28 +0100 Subject: [PATCH 07/24] cage; fix description; --- molviewspec/molviewspec/builder.py | 30 ++++++++++++++++++++++++++++++ molviewspec/molviewspec/nodes.py | 5 ++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/molviewspec/molviewspec/builder.py b/molviewspec/molviewspec/builder.py index 43e3347..f251918 100644 --- a/molviewspec/molviewspec/builder.py +++ b/molviewspec/molviewspec/builder.py @@ -939,6 +939,36 @@ def label( return self def box(self, + *, + center: Vec3, + extent: Vec3, + # TODO: adjust default? + scaling: Vec3[float] | None = 1.0, + as_edges: bool | None = False, + edge_radius: float | None = None, + rotation: Sequence[float] | None = None, + translation: Vec3[float] | None = None, + custom: CustomT = None, + ref: RefT = None, + ) -> Primitives: + # TODO annotation + # """ + # Defines a box + # :param center: center of the box + # :param extent: height and width of the box + # :param scaling: scaling + # :param as_edges: 3d vector describing the scaling. + # :param label_offset: camera-facing offset to prevent overlap with geometry + # :param custom: optional, custom data to attach to this node + # :param ref: optional, reference that can be used to access this node + # :return: this builder + # """ + params = make_params(BoxParams, {"kind": "box", **locals()}) + node = Node(kind="primitive", params=params) + self._add_child(node) + return self + + def cage(self, *, center: Vec3, extent: Vec3, diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index 02ce6b4..4043bd5 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -800,9 +800,8 @@ class Star(BaseModel): class BoxParams(TransformParams): kind: Literal["box"] = "box" - center: Vec3 = Field(description="The center of the box") - # TODO: is this correct meaning? - extent: Vec3 = Field(description="The height and width of the box") + center: Vec3 = Field(description="The center of the box.") + extent: Vec3 = Field(description="The width, the height, and the depth of the box.") # TODO: include in TransformParams instead? scaling: Optional[Vec3[float]] = Field(description="3d vector describing the scaling.") as_edges: Optional[bool] = Field(description="Determine whether to render the box as edges.") From a18e49165d19abb0d4728c46bc245e4e94fe5083 Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Tue, 5 Nov 2024 11:12:58 +0100 Subject: [PATCH 08/24] fixes; cylinder; --- molviewspec/molviewspec/builder.py | 38 ++++++++++++++++++++++++++++-- molviewspec/molviewspec/nodes.py | 7 ++++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/molviewspec/molviewspec/builder.py b/molviewspec/molviewspec/builder.py index f251918..95196f6 100644 --- a/molviewspec/molviewspec/builder.py +++ b/molviewspec/molviewspec/builder.py @@ -12,6 +12,7 @@ from pydantic import BaseModel, PrivateAttr from molviewspec.nodes import ( +# from molviewspec.molviewspec.nodes import ( BoxParams, CameraParams, CanvasParams, @@ -25,6 +26,7 @@ ComponentInlineParams, ComponentSelectorT, CustomT, + CylinderParams, DescriptionFormatT, DistanceMeasurementParams, DownloadParams, @@ -942,8 +944,9 @@ def box(self, *, center: Vec3, extent: Vec3, + color: ColorT | None = None, # TODO: adjust default? - scaling: Vec3[float] | None = 1.0, + scaling: Vec3[float] | None = [1.0, 1.0, 1.0], as_edges: bool | None = False, edge_radius: float | None = None, rotation: Sequence[float] | None = None, @@ -996,4 +999,35 @@ def cage(self, params = make_params(BoxParams, {"kind": "box", **locals()}) node = Node(kind="primitive", params=params) self._add_child(node) - return self \ No newline at end of file + return self + + def cylinder(self, + *, + radius_top: float, + radius_bottom: float, + height: float, + # TODO: theta start/length once clarified + bottom_cap: bool, + top_cap: bool, + color: ColorT | None = None, + rotation: Sequence[float] | None = None, + custom: CustomT = None, + ref: RefT = None, + ) -> Primitives: + # TODO annotation + # """ + # Defines a box + # :param center: center of the box + # :param extent: height and width of the box + # :param scaling: scaling + # :param as_edges: 3d vector describing the scaling. + # :param label_offset: camera-facing offset to prevent overlap with geometry + # :param custom: optional, custom data to attach to this node + # :param ref: optional, reference that can be used to access this node + # :return: this builder + # """ + params = make_params(CylinderParams, {"kind": "cylinder", **locals()}) + node = Node(kind="primitive", params=params) + self._add_child(node) + return self + \ No newline at end of file diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index 4043bd5..b54ec4b 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -701,7 +701,7 @@ class LinesParams(BaseModel): indices: list[int] = Field( description="2N length array of indices into vertices that form lines (l1_1, ll1_2, ...)" ) - line_groups: Optional[list[int]] = Field(description="Assign a number to each triangle to group them.") + line_groups: Optional[list[int]] = Field(description="Assign a number to each line to group them.") group_colors: Optional[Mapping[int, ColorT]] = Field( description="Assign a color to each group. If not assigned, default primitives group color is used. Takes precedence over line_colors." ) @@ -800,6 +800,9 @@ class Star(BaseModel): class BoxParams(TransformParams): kind: Literal["box"] = "box" + # TODO: implement + box_groups: Optional[list[int]] = Field(description="Assign a number to each box to group them.") + color: Optional[ColorT] = Field(description="Default color for the box.") center: Vec3 = Field(description="The center of the box.") extent: Vec3 = Field(description="The width, the height, and the depth of the box.") # TODO: include in TransformParams instead? @@ -810,7 +813,7 @@ class BoxParams(TransformParams): class CylinderParams(BaseModel): kind: Literal["cylinder"] = "cylinder" - center: Vec3 = Field(description="The center of the box") + color: Optional[ColorT] = Field(description="Default color for the box.") radius_top: float = Field(description="The radius of the top of the cylinder top. Radius equal to zero will yield a cone.") radius_bottom: float = Field(description="The radius of the bottom of the cylinder. Radius equal to zero will yield a reversed cone.") height: float = Field(description="The height of the cone.") From d1e9cb98a61656696b44a5b5a2701463e9236ef8 Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Tue, 5 Nov 2024 11:15:03 +0100 Subject: [PATCH 09/24] Vec3 to PrimitivePositionT for all centers --- molviewspec/molviewspec/nodes.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index b54ec4b..ede191d 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -774,7 +774,7 @@ def validate_state_tree(json: str) -> None: # TODO: fields instead of plain types class CircleParams(BaseModel): kind: Literal["circle"] = "circle" - center: Vec3 = Field(description="The center of the circle.") + center: PrimitivePositionT = Field(description="The center of the circle.") # TODO: elaborate names and semantics depending on # how Mol* implements circles # (should be dir_major and dir_minor perhaps as these two here are just Vec3) @@ -788,7 +788,7 @@ class Polygon(BaseModel): class Star(BaseModel): kind: Literal["star"] = "star" - center: Vec3 = Field(description="The center of the star.") + center: PrimitivePositionT = Field(description="The center of the star.") inner_radius: float = Field(description="The inner radius of the star") outer_radius: float = Field(description="The outer radius of the star") # TODO: is this correct meaning? @@ -803,7 +803,7 @@ class BoxParams(TransformParams): # TODO: implement box_groups: Optional[list[int]] = Field(description="Assign a number to each box to group them.") color: Optional[ColorT] = Field(description="Default color for the box.") - center: Vec3 = Field(description="The center of the box.") + center: PrimitivePositionT = Field(description="The center of the box.") extent: Vec3 = Field(description="The width, the height, and the depth of the box.") # TODO: include in TransformParams instead? scaling: Optional[Vec3[float]] = Field(description="3d vector describing the scaling.") @@ -871,13 +871,13 @@ class PyramidParams(BaseModel): class SphereParams(BaseModel): kind: Literal["sphere"] = "sphere" - center: Vec3 = Field(description="The center of the circle.") + center: PrimitivePositionT = Field(description="The center of the circle.") radius: float | PrimitivePositionT = Field(description="The radius of the sphere.") # TODO: class TorusParams(BaseModel): kind: Literal["torus"] = "torus" - center: Vec3 = Field(description="The center of the torus.") + center: PrimitivePositionT = Field(description="The center of the torus.") outer_radius: float = Field(description="The outer radius of the torus") tube_radius: float = Field(description="The tube radius.") # TODO: @@ -890,7 +890,7 @@ class TorusParams(BaseModel): class WedgeParams(BaseModel): kind: Literal["wedge"] = "wedge" - center: Vec3 = Field(description="The center of the wedge.") + center: PrimitivePositionT = Field(description="The center of the wedge.") width: float = Field(description="The width of the wedge.") height: float = Field(description="The height of the wedge.") depth: float = Field(description="The depth of the wedge.") @@ -904,7 +904,7 @@ class EllipsoidParams(BaseModel): direction_major: Vec3[float] = Field(description="Major direction of the ellipsoid.") direction_minor: Vec3[float] = Field(description="Minor direction of the ellipsoid.") direction_normal: Vec3[float] = Field(description="Normal direction of the ellipsoid.") - center: Vec3 = Field(description="The center of the ellipsoid.") + center: PrimitivePositionT = Field(description="The center of the ellipsoid.") # TODO: is the meaning correct radius_scale: Optional[Vec3[float]] = Field(description="3d vector describing the radius scaling.") From 9b72e3c552087937c64b292b5a646616793f42fd Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Tue, 5 Nov 2024 11:18:21 +0100 Subject: [PATCH 10/24] Vec3 to PrimitivePositionT --- molviewspec/molviewspec/nodes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index ede191d..43ead62 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -836,8 +836,8 @@ class ArrowParams(_LineParamsBase): kind: Literal["arrow"] = "arrow" arrow_radius: float = Field(description="The radius (extent) of the arrow.") arrow_height: float = Field(description="The height of the arrow.") - arrow_from: Vec3 = Field(description="Start of the arrow.") - arrow_to: Vec3 = Field(description="End of the arrow.") + arrow_from: PrimitivePositionT = Field(description="Start of the arrow.") + arrow_to: PrimitivePositionT = Field(description="End of the arrow.") SolidKindTypeT = Literal["tetra", "octa", "dodeca", "icosahedron"] From 84e128f56cdcc010e79c89dafbf163060a90c544 Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Mon, 11 Nov 2024 06:33:54 +0100 Subject: [PATCH 11/24] Transform mixin --- molviewspec/molviewspec/nodes.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index 43ead62..64523e9 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -653,7 +653,6 @@ class PrimitiveComponentExpressions(BaseModel): a list of expressions within a specific structure. """ - class PrimitivesParams(BaseModel): color: Optional[ColorT] = Field(description="Default color for primitives in this group") label_color: Optional[ColorT] = Field(description="Default label color for primitives in this group") @@ -797,8 +796,14 @@ class Star(BaseModel): rotation: Optional[Mat3[float]] = Field( description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", ) - -class BoxParams(TransformParams): + +class TransformMixin: + rotation: Optional[Mat3[float]] = Field( + description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", + ) + translation: Optional[Vec3[float]] = Field(description="3d vector describing the translation") + +class BoxParams(TransformMixin): kind: Literal["box"] = "box" # TODO: implement box_groups: Optional[list[int]] = Field(description="Assign a number to each box to group them.") @@ -810,6 +815,7 @@ class BoxParams(TransformParams): as_edges: Optional[bool] = Field(description="Determine whether to render the box as edges.") # TODO: meaning of this? Thickness of edges? edge_radius: Optional[float] = Field(description="The thickness of edges.") + alignment: Literal["axes", "principal-axes"] = "axes" class CylinderParams(BaseModel): kind: Literal["cylinder"] = "cylinder" From e89eb17c622c36ed553b53fda97a80b0c5bf8aa8 Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Mon, 11 Nov 2024 06:36:59 +0100 Subject: [PATCH 12/24] fix cylinder params --- molviewspec/molviewspec/nodes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index 64523e9..dd36325 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -820,9 +820,10 @@ class BoxParams(TransformMixin): class CylinderParams(BaseModel): kind: Literal["cylinder"] = "cylinder" color: Optional[ColorT] = Field(description="Default color for the box.") + bottom: PrimitivePositionT = Field(description="Position of the bottom.") + up: PrimitivePositionT = Field(description="Position of the top.") radius_top: float = Field(description="The radius of the top of the cylinder top. Radius equal to zero will yield a cone.") radius_bottom: float = Field(description="The radius of the bottom of the cylinder. Radius equal to zero will yield a reversed cone.") - height: float = Field(description="The height of the cone.") # TODO: meaning of the following two? Check Sebastian's answers in some of the PRs. theta_start: float = Field(description="TODO") theta_length: float = Field(description="TODO") From 99921cb8f7ed30ea4fc8d2732c61114232ec5c22 Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Mon, 11 Nov 2024 06:41:18 +0100 Subject: [PATCH 13/24] cage --- molviewspec/molviewspec/nodes.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index dd36325..0f87e23 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -797,26 +797,31 @@ class Star(BaseModel): description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", ) -class TransformMixin: +class TransformMixin(BaseModel): rotation: Optional[Mat3[float]] = Field( description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", ) translation: Optional[Vec3[float]] = Field(description="3d vector describing the translation") -class BoxParams(TransformMixin): - kind: Literal["box"] = "box" - # TODO: implement - box_groups: Optional[list[int]] = Field(description="Assign a number to each box to group them.") - color: Optional[ColorT] = Field(description="Default color for the box.") +class RectangularPrismMixin(BaseModel): center: PrimitivePositionT = Field(description="The center of the box.") extent: Vec3 = Field(description="The width, the height, and the depth of the box.") # TODO: include in TransformParams instead? scaling: Optional[Vec3[float]] = Field(description="3d vector describing the scaling.") - as_edges: Optional[bool] = Field(description="Determine whether to render the box as edges.") # TODO: meaning of this? Thickness of edges? edge_radius: Optional[float] = Field(description="The thickness of edges.") alignment: Literal["axes", "principal-axes"] = "axes" - + groups: Optional[list[int]] = Field(description="Assign a number to each box to group them.") + color: Optional[ColorT] = Field(description="Default color for the box.") + +class BoxParams(TransformMixin, RectangularPrismMixin): + kind: Literal["box"] = "box" + # TODO: implement + +class CageParams(TransformMixin, RectangularPrismMixin): + kind: Literal["cage"] = "cage" + type: Literal["as_lines", "as_geometry"] = "as_lines" + class CylinderParams(BaseModel): kind: Literal["cylinder"] = "cylinder" color: Optional[ColorT] = Field(description="Default color for the box.") From 3959aaf3236617fb437bbf8a169a29d683c70b3e Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Thu, 14 Nov 2024 10:32:52 +0100 Subject: [PATCH 14/24] fix center param type; remove as_edges param; remove outdated comment; --- molviewspec/molviewspec/builder.py | 5 ++--- molviewspec/molviewspec/nodes.py | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/molviewspec/molviewspec/builder.py b/molviewspec/molviewspec/builder.py index 95196f6..5b8ae95 100644 --- a/molviewspec/molviewspec/builder.py +++ b/molviewspec/molviewspec/builder.py @@ -942,12 +942,11 @@ def label( def box(self, *, - center: Vec3, + center: PrimitivePositionT, extent: Vec3, color: ColorT | None = None, # TODO: adjust default? scaling: Vec3[float] | None = [1.0, 1.0, 1.0], - as_edges: bool | None = False, edge_radius: float | None = None, rotation: Sequence[float] | None = None, translation: Vec3[float] | None = None, @@ -973,7 +972,7 @@ def box(self, def cage(self, *, - center: Vec3, + center: PrimitivePositionT, extent: Vec3, # TODO: adjust default? scaling: Vec3[float] | None = 1.0, diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index 0f87e23..5e94fd1 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -816,7 +816,6 @@ class RectangularPrismMixin(BaseModel): class BoxParams(TransformMixin, RectangularPrismMixin): kind: Literal["box"] = "box" - # TODO: implement class CageParams(TransformMixin, RectangularPrismMixin): kind: Literal["cage"] = "cage" From c0b83c612bde09bbe350d2b7a63addf32cd3fb0c Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Fri, 15 Nov 2024 09:55:07 +0100 Subject: [PATCH 15/24] change rotation implementation from 9d matrix to angle and axis --- molviewspec/molviewspec/nodes.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index 5e94fd1..523c45f 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -793,13 +793,20 @@ class Star(BaseModel): # TODO: is this correct meaning? point_count: int = Field(description="The number of points the star contains") # TODO: inherit from TransformParams instead? - rotation: Optional[Mat3[float]] = Field( - description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", - ) + # rotation: Optional[Mat3[float]] = Field( + # description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", + # ) class TransformMixin(BaseModel): - rotation: Optional[Mat3[float]] = Field( - description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", + # rotation: Optional[Mat3[float]] = Field( + # description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", + # ) + rotation_axis: Optional[Vec3] = Field( + # TODO: better description + description="Rotation axis" + ) + rotation_radians: Optional[int] = Field( + description="Rotation angle in radians" ) translation: Optional[Vec3[float]] = Field(description="3d vector describing the translation") From 16db139a18d8de9fef3c35d17e5d2582bdef725f Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Fri, 15 Nov 2024 11:10:13 +0100 Subject: [PATCH 16/24] cage support; fix params; --- molviewspec/molviewspec/builder.py | 23 ++++++++++++----------- molviewspec/molviewspec/nodes.py | 2 -- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/molviewspec/molviewspec/builder.py b/molviewspec/molviewspec/builder.py index 5b8ae95..9a28383 100644 --- a/molviewspec/molviewspec/builder.py +++ b/molviewspec/molviewspec/builder.py @@ -14,6 +14,7 @@ from molviewspec.nodes import ( # from molviewspec.molviewspec.nodes import ( BoxParams, + CageParams, CameraParams, CanvasParams, ColorFromSourceParams, @@ -60,7 +61,7 @@ TransparencyInlineParams, Vec3, ) -from molviewspec.utils import get_major_version_tag, make_params +from molviewspec.molviewspec.utils import get_major_version_tag, make_params def create_builder() -> Root: @@ -945,15 +946,14 @@ def box(self, center: PrimitivePositionT, extent: Vec3, color: ColorT | None = None, - # TODO: adjust default? scaling: Vec3[float] | None = [1.0, 1.0, 1.0], - edge_radius: float | None = None, - rotation: Sequence[float] | None = None, + rotation_axis: Vec3[float] | None = None, + rotation_radians: float | None = None, translation: Vec3[float] | None = None, custom: CustomT = None, ref: RefT = None, ) -> Primitives: - # TODO annotation + # TODO: annotation # """ # Defines a box # :param center: center of the box @@ -974,12 +974,13 @@ def cage(self, *, center: PrimitivePositionT, extent: Vec3, - # TODO: adjust default? - scaling: Vec3[float] | None = 1.0, - as_edges: bool | None = False, + color: ColorT | None = None, + scaling: Vec3[float] | None = [1.0, 1.0, 1.0], edge_radius: float | None = None, - rotation: Sequence[float] | None = None, - translation: Vec3[float] | None = None, + rotation_axis: Vec3[float] | None = None, + rotation_radians: float | None = None, + translation: Vec3[float] | None = None, + type: Literal["as_lines", "as_geometry"] = "as_lines", custom: CustomT = None, ref: RefT = None, ) -> Primitives: @@ -995,7 +996,7 @@ def cage(self, # :param ref: optional, reference that can be used to access this node # :return: this builder # """ - params = make_params(BoxParams, {"kind": "box", **locals()}) + params = make_params(CageParams, {"kind": "cage", **locals()}) node = Node(kind="primitive", params=params) self._add_child(node) return self diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index 523c45f..b9e47bd 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -815,8 +815,6 @@ class RectangularPrismMixin(BaseModel): extent: Vec3 = Field(description="The width, the height, and the depth of the box.") # TODO: include in TransformParams instead? scaling: Optional[Vec3[float]] = Field(description="3d vector describing the scaling.") - # TODO: meaning of this? Thickness of edges? - edge_radius: Optional[float] = Field(description="The thickness of edges.") alignment: Literal["axes", "principal-axes"] = "axes" groups: Optional[list[int]] = Field(description="Assign a number to each box to group them.") color: Optional[ColorT] = Field(description="Default color for the box.") From 9a298e2571abe0921e4564251491720076894342 Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Sat, 16 Nov 2024 06:50:18 +0100 Subject: [PATCH 17/24] cylinder fix params --- molviewspec/molviewspec/builder.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/molviewspec/molviewspec/builder.py b/molviewspec/molviewspec/builder.py index 9a28383..fbf58e5 100644 --- a/molviewspec/molviewspec/builder.py +++ b/molviewspec/molviewspec/builder.py @@ -1003,14 +1003,15 @@ def cage(self, def cylinder(self, *, + bottom: Vec3[float], + up: Vec3[float], radius_top: float, radius_bottom: float, - height: float, - # TODO: theta start/length once clarified bottom_cap: bool, top_cap: bool, color: ColorT | None = None, - rotation: Sequence[float] | None = None, + rotation_axis: Vec3[float] | None = None, + rotation_radians: float | None = None, custom: CustomT = None, ref: RefT = None, ) -> Primitives: From e2d1ba824b11cb41b10e26850f38c321719a532d Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Sat, 16 Nov 2024 07:04:57 +0100 Subject: [PATCH 18/24] cylinder to cone; no theta --- molviewspec/molviewspec/builder.py | 2 +- molviewspec/molviewspec/nodes.py | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/molviewspec/molviewspec/builder.py b/molviewspec/molviewspec/builder.py index fbf58e5..762ed22 100644 --- a/molviewspec/molviewspec/builder.py +++ b/molviewspec/molviewspec/builder.py @@ -1001,7 +1001,7 @@ def cage(self, self._add_child(node) return self - def cylinder(self, + def cone(self, *, bottom: Vec3[float], up: Vec3[float], diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index b9e47bd..29c8961 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -833,9 +833,9 @@ class CylinderParams(BaseModel): up: PrimitivePositionT = Field(description="Position of the top.") radius_top: float = Field(description="The radius of the top of the cylinder top. Radius equal to zero will yield a cone.") radius_bottom: float = Field(description="The radius of the bottom of the cylinder. Radius equal to zero will yield a reversed cone.") - # TODO: meaning of the following two? Check Sebastian's answers in some of the PRs. - theta_start: float = Field(description="TODO") - theta_length: float = Field(description="TODO") + # TODO: Implement start end of the arc (theta start end) + # theta_start: float = Field(description="TODO") + # theta_length: float = Field(description="TODO") # TODO: type for rotation as field rotation: Optional[Mat3[float]] = Field( description="9d vector describing the rotation, in a column major (j * 3 + i indexing) format, this is equivalent to Fortran-order in numpy, to be multiplied from the left", @@ -843,6 +843,11 @@ class CylinderParams(BaseModel): bottom_cap: bool = Field(description="Determine whether to cap the top of the cylinder.") top_cap: bool = Field(description="Determine whether to cap the bottom of the cylinder.") + +# TODO: instead of theta - tube? +# addTube in mol*, params accordingly + + # NOTE: arrow should be derived from line perhaps? # This allows us to make use of inheritance From b7712ac6f2d725f2dc0322a57f9a3a4f9c7ff4c6 Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Sat, 16 Nov 2024 09:33:21 +0100 Subject: [PATCH 19/24] draft for circle --- molviewspec/molviewspec/builder.py | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/molviewspec/molviewspec/builder.py b/molviewspec/molviewspec/builder.py index 762ed22..34f7bae 100644 --- a/molviewspec/molviewspec/builder.py +++ b/molviewspec/molviewspec/builder.py @@ -17,6 +17,7 @@ CageParams, CameraParams, CanvasParams, + CircleParams, ColorFromSourceParams, ColorFromUriParams, ColorInlineParams, @@ -1001,6 +1002,42 @@ def cage(self, self._add_child(node) return self + # def ellipsoid( + # self, + # *, + # direction_major: Vec3[float], + # direction_minor: Vec3[float], + # direction_normal: Vec3[float] = Field(description="Normal direction of the ellipsoid.") + # center: PrimitivePositionT = Field(description="The center of the ellipsoid.") + # # TODO: is the meaning correct + # radius_scale: Optional[Vec3[float]] = Field(description="3d vector describing the radius scaling.") + + + # ) + + def circle(self, + *, + center: PrimitivePositionT, + major_axis: PrimitivePositionT, + minor_axis: PrimitivePositionT + ): + # TODO annotation + # """ + # Defines a box + # :param center: center of the box + # :param extent: height and width of the box + # :param scaling: scaling + # :param as_edges: 3d vector describing the scaling. + # :param label_offset: camera-facing offset to prevent overlap with geometry + # :param custom: optional, custom data to attach to this node + # :param ref: optional, reference that can be used to access this node + # :return: this builder + # """ + params = make_params(CircleParams, {"kind": "circle", **locals()}) + node = Node(kind="primitive", params=params) + self._add_child(node) + return self + def cone(self, *, bottom: Vec3[float], From 6c53d6245f2107fdf97251b3351d7ec980ae8d38 Mon Sep 17 00:00:00 2001 From: Aliaksei <52605565+aliaksei-chareshneu@users.noreply.github.com> Date: Sat, 16 Nov 2024 09:33:53 +0100 Subject: [PATCH 20/24] Update molviewspec/molviewspec/nodes.py Co-authored-by: David Sehnal --- molviewspec/molviewspec/nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index 29c8961..0c73b01 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -824,7 +824,7 @@ class BoxParams(TransformMixin, RectangularPrismMixin): class CageParams(TransformMixin, RectangularPrismMixin): kind: Literal["cage"] = "cage" - type: Literal["as_lines", "as_geometry"] = "as_lines" + render_style: Literal["lines", "geometry"] = "lines" class CylinderParams(BaseModel): kind: Literal["cylinder"] = "cylinder" From b7e191c3e6892765db4f4fb3e903317e57dc9591 Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Sat, 16 Nov 2024 10:18:50 +0100 Subject: [PATCH 21/24] examples --- _examples/build_example.py | 113 +++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 _examples/build_example.py diff --git a/_examples/build_example.py b/_examples/build_example.py new file mode 100644 index 0000000..9263813 --- /dev/null +++ b/_examples/build_example.py @@ -0,0 +1,113 @@ +from molviewspec.molviewspec.builder import create_builder +from molviewspec.molviewspec.nodes import ComponentExpression + + +builder = create_builder() + +download = builder.download(url="https://www.ebi.ac.uk/pdbe/entry-files/download/1tqn_updated.cif") +structure = download.parse(format="mmcif").model_structure() + +component1 = structure.component(selector="ligand").representation(type="ball_and_stick") +structure.component(selector="polymer").representation(type="cartoon") +structure.primitives().box(center=ComponentExpression(auth_seq_id=508), extent=[5,5,5], color="green") # or .cage + +# TODO: debug at frontend then try these params +builder.primitives().box( + center=[0.1, 0.1, 0.1], + extent=[1.0, 1.0, 1.0], + color="red", + scaling=[10.0, 1.0, 1.0], + translation=[10.0, 10.0, 10.0], + rotation_axis=[0.0, 1.0, 0.0], + rotation_radians=1.0 +) + +structure.primitives().cage( + # center=[0.5, 0.5, 0.5], + center=ComponentExpression(auth_seq_id=508), + extent=[5.0, 3.0, 18.0], + color="orange", + scaling=[10.0, 1.0, 1.0], + translation=[10.0, 10.0, 10.0], + rotation_axis=[0.0, 1.0, 0.0], + rotation_radians=1.0, + edge_radius=7.5, + # TODO: as geometry + type="as_lines" +) + +structure.primitives().cone( + radius_bottom=1.5, + radius_top=2.5, + bottom_cap=True, + top_cap=False, + color="magenta", + bottom=[1, 1, 1], + up=[2,5,10] + # bottom=ComponentExpression(auth_seq_id=508), + # up=ComponentExpression(auth_seq_id=507) +) + +builder.primitives().box( + center=[10, 10, 10], + extent=[2.0, 3.0, 10.0], + color="blue" +) + + +# let's throw in some lines and labels that intersect each face in the middle +# ( +# builder.primitives(color="blue", label_color="blue", tooltip="Generic Axis", transparency=0.5) +# # chain primitives to create desired visuals +# .line(start=(-0.5, 0.5, 0.5), end=(1.5, 0.5, 0.5), thickness=0.05, color="red", tooltip="### Axis\nX") +# # .label(position=(-0.5, 0.5, 0.5), text="X", label_size=0.33, label_color="orange") +# .line(start=(0.5, -0.5, 0.5), end=(0.5, 1.5, 0.5), thickness=0.05, color="green", tooltip="### Axis\nY") +# # .label(position=(0.5, -0.5, 0.5), text="Y", label_size=0.33, label_color="blue") +# .line(start=(0.5, 0.5, -0.5), end=(0.5, 0.5, 1.5), thickness=0.05, color="magenta") +# # .label(position=(0.5, 0.5, -0.5), text="Z", label_size=0.33, color="yellow") +# ) +# builder.primitives().mesh( +# vertices=[ +# 0.0, 0.0, 0.0, +# 1.0, 0.0, 0.0, +# 1.0, 1.0, 0.0, +# 0.0, 1.0, 0.0, +# 0.0, 0.0, 1.0, +# 1.0, 0.0, 1.0, +# 1.0, 1.0, 1.0, +# 0.0, 1.0, 1.0, +# ], +# indices=[ +# # bottom +# 0, 2, 1, 0, 3, 2, +# # top +# 4, 5, 6, 4, 6, 7, +# # front +# 0, 1, 5, 0, 5, 4, +# # back +# 2, 3, 7, 2, 7, 6, +# # left +# 0, 7, 3, 0, 4, 7, +# # right +# 1, 2, 6, 1, 6, 5, +# ], +# triangle_groups=[0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5], +# group_colors={0: "red", 1: "green", 2: "blue", 3: "yellow", 4: "magenta", 5: "cyan"}, +# group_tooltips={0: "### Side\nbottom", 1: "### Side\ntop", 2: "### Side\nfront", 3: "### Side\nback", 4: "### Side\nleft", 5: "### Side\nright"}, +# show_wireframe=True, +# wireframe_radius=2, +# wireframe_color="black", +# ) +# # let's throw in some lines and labels that intersect each face in the middle +# ( +# builder.primitives(color="blue", label_color="blue", tooltip="Generic Axis", transparency=0.5) +# # chain primitives to create desired visuals +# .line(start=(-0.5, 0.5, 0.5), end=(1.5, 0.5, 0.5), thickness=0.05, color="red", tooltip="### Axis\nX") +# .label(position=(-0.5, 0.5, 0.5), text="X", label_size=0.33, label_color="red") +# .line(start=(0.5, -0.5, 0.5), end=(0.5, 1.5, 0.5), thickness=0.05, color="green", tooltip="### Axis\nY") +# .label(position=(0.5, -0.5, 0.5), text="Y", label_size=0.33, label_color="green") +# .line(start=(0.5, 0.5, -0.5), end=(0.5, 0.5, 1.5), thickness=0.05) +# .label(position=(0.5, 0.5, -0.5), text="Z", label_size=0.33) +# ) + +builder.save_state(destination='./_examples/state.mvsj') \ No newline at end of file From 4ae6e3117bbe661a16b2facbbc725e4b497c430f Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Sat, 16 Nov 2024 10:21:16 +0100 Subject: [PATCH 22/24] upd example --- _examples/build_example.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/_examples/build_example.py b/_examples/build_example.py index 9263813..856a534 100644 --- a/_examples/build_example.py +++ b/_examples/build_example.py @@ -42,10 +42,10 @@ bottom_cap=True, top_cap=False, color="magenta", - bottom=[1, 1, 1], - up=[2,5,10] - # bottom=ComponentExpression(auth_seq_id=508), - # up=ComponentExpression(auth_seq_id=507) + # bottom=[1, 1, 1], + # up=[2,5,10] + bottom=ComponentExpression(auth_seq_id=508), + up=ComponentExpression(auth_seq_id=399) ) builder.primitives().box( From a20d3bd4a02e0f9f0001d4afbcaa96c55bb6e052 Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Wed, 20 Nov 2024 07:14:18 +0100 Subject: [PATCH 23/24] ellipsoid prototype (tested, color TODO); ellipsis draft (WIP); --- molviewspec/molviewspec/builder.py | 49 ++++++++++++++++++++---------- molviewspec/molviewspec/nodes.py | 6 ++-- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/molviewspec/molviewspec/builder.py b/molviewspec/molviewspec/builder.py index 34f7bae..3087784 100644 --- a/molviewspec/molviewspec/builder.py +++ b/molviewspec/molviewspec/builder.py @@ -17,7 +17,7 @@ CageParams, CameraParams, CanvasParams, - CircleParams, + EllipsisParams, ColorFromSourceParams, ColorFromUriParams, ColorInlineParams, @@ -32,6 +32,7 @@ DescriptionFormatT, DistanceMeasurementParams, DownloadParams, + EllipsoidParams, FocusInlineParams, LabelFromSourceParams, LabelFromUriParams, @@ -1002,22 +1003,37 @@ def cage(self, self._add_child(node) return self - # def ellipsoid( - # self, - # *, - # direction_major: Vec3[float], - # direction_minor: Vec3[float], - # direction_normal: Vec3[float] = Field(description="Normal direction of the ellipsoid.") - # center: PrimitivePositionT = Field(description="The center of the ellipsoid.") - # # TODO: is the meaning correct - # radius_scale: Optional[Vec3[float]] = Field(description="3d vector describing the radius scaling.") - - - # ) + # TODO: sphere + def ellipsoid( + self, + *, + color: ColorT | None = None, + direction_major: Vec3[float], + direction_minor: Vec3[float], + center: PrimitivePositionT, + radius_scale: Vec3[float] | None = None + ): + # TODO annotation + # """ + # Defines a box + # :param center: center of the box + # :param extent: height and width of the box + # :param scaling: scaling + # :param as_edges: 3d vector describing the scaling. + # :param label_offset: camera-facing offset to prevent overlap with geometry + # :param custom: optional, custom data to attach to this node + # :param ref: optional, reference that can be used to access this node + # :return: this builder + # """ + params = make_params(EllipsoidParams, {"kind": "ellipsoid", **locals()}) + node = Node(kind="primitive", params=params) + self._add_child(node) + return self - def circle(self, + def ellipsis(self, *, center: PrimitivePositionT, + color: ColorT | None = None, major_axis: PrimitivePositionT, minor_axis: PrimitivePositionT ): @@ -1033,12 +1049,13 @@ def circle(self, # :param ref: optional, reference that can be used to access this node # :return: this builder # """ - params = make_params(CircleParams, {"kind": "circle", **locals()}) + params = make_params(EllipsisParams, {"kind": "circle", **locals()}) node = Node(kind="primitive", params=params) self._add_child(node) return self - def cone(self, + # TODO: cone + def cylinder(self, *, bottom: Vec3[float], up: Vec3[float], diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index 0c73b01..a942f61 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -771,7 +771,7 @@ def validate_state_tree(json: str) -> None: State.parse_raw(json) # TODO: fields instead of plain types -class CircleParams(BaseModel): +class EllipsisParams(BaseModel): kind: Literal["circle"] = "circle" center: PrimitivePositionT = Field(description="The center of the circle.") # TODO: elaborate names and semantics depending on @@ -920,13 +920,11 @@ class WedgeParams(BaseModel): ) class EllipsoidParams(BaseModel): - # TODO: adjust based on frontend implementation kind: Literal["ellipsoid"] = "ellipsoid" direction_major: Vec3[float] = Field(description="Major direction of the ellipsoid.") direction_minor: Vec3[float] = Field(description="Minor direction of the ellipsoid.") - direction_normal: Vec3[float] = Field(description="Normal direction of the ellipsoid.") + # direction_normal: Vec3[float] = Field(description="Normal direction of the ellipsoid.") center: PrimitivePositionT = Field(description="The center of the ellipsoid.") - # TODO: is the meaning correct radius_scale: Optional[Vec3[float]] = Field(description="3d vector describing the radius scaling.") # TODO: From 617a8f3a3c7c412dfaea8cad0b2969d82a87482f Mon Sep 17 00:00:00 2001 From: Aliaksei Chareshneu Date: Wed, 20 Nov 2024 07:16:34 +0100 Subject: [PATCH 24/24] fix: add missing color params --- molviewspec/molviewspec/nodes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/molviewspec/molviewspec/nodes.py b/molviewspec/molviewspec/nodes.py index a942f61..06458dc 100644 --- a/molviewspec/molviewspec/nodes.py +++ b/molviewspec/molviewspec/nodes.py @@ -773,6 +773,7 @@ def validate_state_tree(json: str) -> None: # TODO: fields instead of plain types class EllipsisParams(BaseModel): kind: Literal["circle"] = "circle" + color: Optional[ColorT] = Field(description="Default color for the circle.") center: PrimitivePositionT = Field(description="The center of the circle.") # TODO: elaborate names and semantics depending on # how Mol* implements circles @@ -921,6 +922,7 @@ class WedgeParams(BaseModel): class EllipsoidParams(BaseModel): kind: Literal["ellipsoid"] = "ellipsoid" + color: Optional[ColorT] = Field(description="Default color for the box.") direction_major: Vec3[float] = Field(description="Major direction of the ellipsoid.") direction_minor: Vec3[float] = Field(description="Minor direction of the ellipsoid.") # direction_normal: Vec3[float] = Field(description="Normal direction of the ellipsoid.")