"""
Abstract base classes for UPXO geometric entities.
Usage
-----
from upxo.geoEntities.bases import UPXO_Point, UPXO_Edge
Classes
-------
UPXO_Point : Abstract base class for 2D and 3D point objects.
UPXO_Edge : Abstract base class for 2D and 3D edge objects.
Notes
-----
Concrete implementations (e.g., ``point2d``, ``edge2d``) must override all
abstract methods. The base classes define the common interface contract only
and carry no executable logic.
"""
from abc import ABC, abstractmethod
[docs]
class UPXO_Point(ABC):
"""
Abstract base class for UPXO point entities.
Defines the minimum interface that all concrete 2D and 3D point
implementations must satisfy.
Attributes
----------
x : float
y : float
pln : str
Plane identifier (e.g. ``'ij'``).
f : object
Reserved for future use.
"""
__slots__ = ('x', 'y', 'pln', 'f')
@abstractmethod
def __init__(self, x=.0, y=.0, pln='ij'):
"""Initialise the point at coordinates (x, y)."""
pass
@abstractmethod
def __repr__(self):
"""Return a developer-readable string representation."""
pass
@abstractmethod
def __eq__(self, plist, *, use_tol=True):
"""Check if the two points are coincident."""
pass
@abstractmethod
def __ne__(self, plist, *, use_tol=True):
"""Check if the two points are not coincident."""
pass
[docs]
@abstractmethod
def add(self, distances, update=True, throw=False,
mydecatlen2NUM='taxx'):
"""Translate this point by the given distances."""
pass
@abstractmethod
def __mul__(self, f, update=True, throw=False):
"""
Multiple f to point coord & update self or return new point objects.
All descriptions in parameters below, naturally extend to 3D.
Parameters
----------
f: list of multiplication factors. Depending on d, functionaliy changes
as below.
* [1, 2, 3, 4]: Each entry is multipled to both x and y. 4 new
point objects gets created.
* [[1, 2], [3, 4]]: [1, 2] denote first set of x and y distances.
They get multipled with self.x and self.y to make a new point.
Similar operation extewnds to [3, 4]. Two new points are created.
* [[1, 2, 3, 4], [5, 6, 7, 8]]: These are X and Y arrays. Each x
and y in X and Y, gets multipled with self.x and self.y to make n
points, where n = len(d[0]).
* [po1, po2, po3]: List of point objects. Point objects could be
2D or 3D. UPXO, GMSH, VTK, PyVista, Shapely types are allowed.
update: If True and if f is either K or Iterable(P, Q), where, K, P and
Q are dth.dt.NUMBERS, self will be updated as self.x*K and self.y*K
or self.x*P and self.y*Q.
throw: If True and if additional conditions provided in update are
atisfied, then the deepcopy of the point will be returned. If,
however, update is False, a new point with coordiates self.x*K and
self.y*K or self.x*P and self.y*Q, shall be created and returned.
"""
pass
@abstractmethod
def distance(self, plist=None):
"""Calculate the EUclidean distance between self and list of points."""
pass
[docs]
@abstractmethod
def distance(self, plist=None):
"""Calculate the EUclidean distance between self and list of points."""
pass
[docs]
class UPXO_Edge(ABC):
"""
Abstract base class for UPXO edge (line segment) entities.
Defines the common interface contract for all concrete 2D and 3D edge
implementations. Subclasses store their start and end point references
as ``i`` and ``j``.
Attributes
----------
i : object
Start point of the edge.
j : object
End point of the edge.
"""
__slots__ = ('i', 'j', )
@abstractmethod
def __init__(self):
"""Initialise the edge."""
pass
@abstractmethod
def __repr__(self):
"""Return a developer-readable string representation."""
pass
@abstractmethod
def __eq__(self, elist):
"""Test equality against one or more edges."""
pass
@abstractmethod
def __ne__(self, elist):
"""Test inequality against one or more edges."""
pass
@property
@abstractmethod
def mid(self):
"""Unique identifier (e.g. Python id) of this edge object."""
pass
@property
@abstractmethod
def ang(self):
"""Orientation angle of the edge in degrees."""
pass
@property
@abstractmethod
def length(self):
"""Euclidean length of the edge."""
pass
[docs]
@classmethod
def by_coord(cls, start_point, end_point):
"""Construct an edge from two coordinate pairs or point objects."""
pass
[docs]
@classmethod
def by_loc_len_ang(cls, *, ref='i', loc=[0, 0, 0],
length=1, ang=0, degree=True):
"""Construct an edge from a reference point, length, and angle."""
pass
[docs]
@abstractmethod
def distance_to_points(self, *, plist=None):
"""Calculate distances from this edge to a list of points."""
pass
[docs]
@abstractmethod
def distance_to_edges(self, *, elist=None,
method='ref', refi='mid', refj='mid'):
"""Calculate distances from this edge to a list of other edges."""
pass
[docs]
@abstractmethod
def translate_by(self, *, vector=None, dist=None,
update=False, throw=True):
"""Translate this edge by a displacement vector or scalar distance."""
pass
[docs]
@abstractmethod
def translate_to(self, *, ref='i', point=None, update=False, throw=True):
"""Translate this edge so that the reference endpoint lands on ``point``."""
pass
[docs]
@abstractmethod
def rotate_about(self, *, axis=None, angle=0, degree=True,
update=False, throw=True):
"""Rotate this edge about an axis by the given angle."""
pass
[docs]
@abstractmethod
def attach_mp(self, *, mp=None, name=None):
"""Attach a material-property object to this edge under ``name``."""
self.mp[name] = mp
[docs]
@abstractmethod
def attach_xtal(self, *, xtals=None):
"""Associate crystal objects with this edge."""
pass
[docs]
@abstractmethod
def find_neigh_point_by_distance(self, *, plist=None, plane='xy', r=0):
"""Find all points within radius ``r`` of this edge."""
pass
[docs]
@abstractmethod
def find_neigh_point_by_count(self, *, plist=None, n=None,
plane='xy'):
"""Find the ``n`` nearest points to this edge."""
pass
[docs]
@abstractmethod
def find_neigh_mulpoint_by_distance(self, *, mplist=None,
plane='xy', r=0, tolf=-1):
"""Find all mulpoint objects within radius ``r`` of this edge."""
pass
[docs]
@abstractmethod
def find_neigh_edge_by_distance(self, *, elist=None,
plane='xy', refloc='starting', r=0):
"""Find all edges whose reference location is within radius ``r``."""
pass
[docs]
@abstractmethod
def find_neigh_muledge_by_distance(self, *, melist=None,
plane='xy', refloc='starting', r=0):
"""Find all muledge objects within radius ``r``."""
pass
[docs]
@abstractmethod
def find_neigh_xtal_by_distance(self, *, xlist=None,
plane='xy', refloc='starting', r=0):
"""Find all crystal objects within radius ``r`` of this edge."""
pass
[docs]
@abstractmethod
def set_gmsh_props(self, prop_dict):
"""Attach GMSH mesh properties to this edge."""
pass
[docs]
@abstractmethod
def make_shapely(self):
"""Return a Shapely geometry object for this edge."""
pass
[docs]
@abstractmethod
def make_vtk(self):
"""Return a VTK geometry object for this edge."""
pass
@property
@abstractmethod
def coords(self):
"""Endpoint coordinates as a NumPy array."""
return np.array([self.x, self.y])
[docs]
@abstractmethod
def array_translation(self, *,
ncopies=10,
vector=[[0, 0, 0], [0, 0, 1]],
spacing='constant'):
"""Generate an array of translated copies of this edge."""
pass
[docs]
@abstractmethod
def lies_on_which_edge(self, *, elist=None, consider_ends=True):
"""Determine which edges from ``elist`` this edge lies on."""
pass
[docs]
@abstractmethod
def lies_in_which_xtal(self, *, xlist=None,
cosider_boundary=True,
consider_boundary_ends=True):
"""Determine which crystal from ``xlist`` contains this edge."""
pass