Source code for molsysviewer.shapes.triangle_faces

from __future__ import annotations

from typing import Iterable, Sequence


[docs] class TriangleFaces: def __init__(self, view) -> None: self._view = view @staticmethod def _normalize_vertices(vertices: Iterable[Sequence[float]] | None) -> list[list[list[float]]]: if vertices is None: return [] normalized: list[list[list[float]]] = [] for tri in vertices: tri_list = list(tri) if len(tri_list) == 3 and all(isinstance(v, Sequence) and len(v) == 3 for v in tri_list): normalized.append( [ [float(tri_list[0][0]), float(tri_list[0][1]), float(tri_list[0][2])], [float(tri_list[1][0]), float(tri_list[1][1]), float(tri_list[1][2])], [float(tri_list[2][0]), float(tri_list[2][1]), float(tri_list[2][2])], ] ) elif len(tri_list) == 9: normalized.append( [ [float(tri_list[0]), float(tri_list[1]), float(tri_list[2])], [float(tri_list[3]), float(tri_list[4]), float(tri_list[5])], [float(tri_list[6]), float(tri_list[7]), float(tri_list[8])], ] ) else: raise ValueError( "Each triangle must be [[x1,y1,z1],[x2,y2,z2],[x3,y3,z3]] or a list of 9 numbers" ) return normalized @staticmethod def _normalize_triplets(atom_triplets: Iterable[Sequence[int]] | None) -> list[list[int]]: if atom_triplets is None: return [] normalized: list[list[int]] = [] for tri in atom_triplets: if len(tri) != 3: raise ValueError("Each entry in atom_triplets must have 3 indices") normalized.append([int(tri[0]), int(tri[1]), int(tri[2])]) return normalized @staticmethod def _normalize_optional_list(values, n: int, cast): if values is None: return None if isinstance(values, (str, bytes)): return [cast(values)] * n try: seq = list(values) except TypeError: return [cast(values)] * n if len(seq) not in (1, n): raise ValueError(f"Expected 1 or {n} values, got {len(seq)}") if len(seq) == 1: return [cast(seq[0])] * n return [cast(v) for v in seq]
[docs] def add_triangle_faces( self, *, vertices: Iterable[Sequence[float]] | None = None, atom_triplets: Iterable[Sequence[int]] | None = None, colors: int | Sequence[int] = 0xCCCCCC, alpha: float = 1.0, labels: Sequence[str] | str | None = None, draw_edges: bool | None = None, edge_radius: float | None = None, edge_color: int | None = None, show_normals: bool | None = None, normal_length: float | None = None, normal_color: int | None = None, tag: str | None = None, ): """Add custom triangle faces using coordinates or atom indices. Extra parameters: - draw_edges: draw edges as thin cylinders (`edge_radius`/`edge_color`). - show_normals: draw normals as arrows (`normal_length`/`normal_color`). """ vertices_list = self._normalize_vertices(vertices) atom_triplets_list = self._normalize_triplets(atom_triplets) if not vertices_list and not atom_triplets_list: raise ValueError("You must provide vertices or atom_triplets") n = len(vertices_list) if vertices_list else len(atom_triplets_list) colors_list = self._normalize_optional_list(colors, n, int) labels_list = self._normalize_optional_list(labels, n, str) options: dict = {"alpha": float(alpha)} if vertices_list: options["vertices"] = vertices_list if atom_triplets_list: options["atom_triplets"] = atom_triplets_list if colors_list is not None: options["colors"] = colors_list if labels_list is not None: options["labels"] = labels_list if draw_edges is not None: options["draw_edges"] = bool(draw_edges) if edge_radius is not None: options["edge_radius"] = float(edge_radius) if edge_color is not None: options["edge_color"] = int(edge_color) if show_normals is not None: options["show_normals"] = bool(show_normals) if normal_length is not None: options["normal_length"] = float(normal_length) if normal_color is not None: options["normal_color"] = int(normal_color) tag = tag or self._view._next_layer_tag() # noqa: SLF001 options["tag"] = tag self._view._send({"op": "add_triangle_faces", "options": options}) if tag not in self._view._layers: # noqa: SLF001 from ..layers import Layer self._view._layers[tag] = Layer(self._view, tag, kind="shape", meta={}) # noqa: SLF001 return self._view._layers[tag] # noqa: SLF001