import typing
import numpy as np
from scipy.spatial.distance import euclidean
from stk._internal.building_block import BuildingBlock
from stk._internal.topology_graphs.edge import Edge
from stk._internal.topology_graphs.vertex import Vertex
[docs]
class CycleVertex(Vertex):
"""
Represents a vertex in a macrocycle.
"""
def __init__(
self,
id: int,
position: typing.Union[np.ndarray, tuple[float, float, float]],
flip: bool,
angle: float,
) -> None:
"""
Initialize a :class:`.CycleVertex` instance.
Parameters:
id:
The id of the vertex.
position:
The position of the vertex.
flip:
If ``True``, the orientation of building blocks placed
by the vertex will be flipped.
angle:
The position of the vertex along the cycle, specified
by the `angle`.
"""
super().__init__(id, position)
self._flip = flip
self._angle = angle
[docs]
def clone(self) -> typing.Self:
clone = super().clone()
clone._flip = self._flip
clone._angle = self._angle
return clone
[docs]
def get_flip(self) -> bool:
"""
Return ``True`` if the vertex flips building blocks it places.
Returns:
``True`` if the vertex flips building blocks it places.
"""
return self._flip
[docs]
def place_building_block(
self,
building_block: BuildingBlock,
edges: tuple[Edge, ...],
) -> np.ndarray:
assert building_block.get_num_functional_groups() == 2, (
f"{building_block} needs to have exactly 2 functional "
"groups but has "
f"{building_block.get_num_functional_groups()}."
)
building_block = building_block.with_centroid(
position=self._position,
atom_ids=building_block.get_placer_ids(),
)
fg0, fg1 = building_block.get_functional_groups()
fg0_position = building_block.get_centroid(
atom_ids=fg0.get_placer_ids(),
)
fg1_position = building_block.get_centroid(
atom_ids=fg1.get_placer_ids(),
)
return (
building_block.with_rotation_between_vectors(
start=fg1_position - fg0_position,
target=np.array([-1 if self._flip else 1, 0, 0]),
origin=self._position,
)
.with_rotation_about_axis(
angle=self._angle - (np.pi / 2),
axis=np.array([0, 0, 1]),
origin=self._position,
)
.get_position_matrix()
)
[docs]
def map_functional_groups_to_edges(
self,
building_block: BuildingBlock,
edges: tuple[Edge, ...],
) -> dict[int, int]:
fg0_position = building_block.get_centroid(
atom_ids=next(
building_block.get_functional_groups()
).get_placer_ids(),
)
def fg0_distance(edge: Edge) -> float:
return euclidean(edge.get_position(), fg0_position)
edge0 = min(edges, key=fg0_distance)
return {
0: edge0.get_id(),
1: (edges[1].get_id() if edge0 is edges[0] else edges[0].get_id()),
}
def __str__(self) -> str:
return (
f"Vertex(id={self._id}, "
f"position={tuple(self._position.tolist())}, "
f"flip={self._flip}, "
f"angle={self._angle})"
)