Source code for vibesketch.document.surfaces

"""Editable schematic surface partners and labelled grafts (#140)."""

from __future__ import annotations

import copy
import math
from typing import Any

from pydantic import ValidationError

from vibesketch.document.model import Document, Surface
from vibesketch.render.nanostructures import DepictionRefused
from vibesketch.render.surfaces import anchor_points, canvas_image, surface_of

SurfaceRefused = DepictionRefused


[docs] def records(document: Document) -> list[Surface]: """Re-derive surfaces and their attached generic identities from the current drawing.""" from vibesketch.document.generics import attachment_labels, cxsmiles from vibesketch.document.ket import read_scene generics = {g.cxsmiles: g.id for g in document.generics if g.kind == "labelled"} found: list[Surface] = [] canvas: Any = document.canvas.data objects = [ (n, s) for n in canvas.get("root", {}).get("nodes", []) if isinstance(n, dict) and (s := surface_of(n)) is not None ] if not objects: return [] scene = read_scene(document.canvas.data) for node, surface in objects: if surface.id in {s.id for s in found}: raise SurfaceRefused( f"duplicate surface id {surface.id}; give copied objects distinct ids" ) for name, (x, y, _, _) in anchor_points(node).items(): attached = [] for fragment in scene.fragments: mol = fragment.figure if mol is None or not attachment_labels(mol): continue gid = generics.get(cxsmiles(mol)) if gid is None: continue conf = mol.GetConformer() if any( a.HasProp("attachmentLabel") and math.hypot( conf.GetAtomPosition(a.GetIdx()).x - x, conf.GetAtomPosition(a.GetIdx()).y - y, ) < 1e-4 for a in mol.GetAtoms() ): attached.append(gid) surface.anchors[name].generics = sorted(set(attached)) found.append(surface) return found
[docs] def with_surface( document: Document, parameters: Surface | dict[str, Any], *, at: tuple[float, float] | None = None, width: float = 8, height: float = 3, ) -> Document: """Add or replace an object by id. Geometry is drawing units, never a physical model.""" from vibesketch.document.derive import derive from vibesketch.document.formats import _points try: surface = Surface.model_validate(parameters) except ValidationError as error: raise SurfaceRefused(str(error)) from None canvas: Any = copy.deepcopy(document.canvas.data) or {"root": {"nodes": []}} nodes = canvas.setdefault("root", {}).setdefault("nodes", []) old = next( ( n for n in nodes if isinstance(n, dict) and (s := surface_of(n)) is not None and s.id == surface.id ), None, ) if at is None: if old is not None: at = (old["boundingBox"]["x"], old["boundingBox"]["y"]) else: points = _points(canvas) at = (max(p[0] for p in points) + 2, max(p[1] for p in points)) if points else (0, 0) node = canvas_image(surface, {s.id: s for s in document.species}, at, width, height) if old is not None: nodes[nodes.index(old)] = node else: nodes.append(node) canvas.setdefault("ket_version", "2.0.0") updated = document.model_copy( update={"canvas": document.canvas.model_copy(update={"data": canvas})} ) return derive(updated).document
[docs] def attach_to_surface( document: Document, generic: str, surface: str, anchor: str = "top" ) -> Document: """Join a labelled generic to a surface anchor using the nanostructure grafting path.""" from vibesketch.document.nanostructures import attach_to_picture return attach_to_picture(document, generic, surface, anchor)