Source code for g3d.mesh.geo.edge

"""mesh building block - Edge class definition"""
import os
from typing import Any, Union, Tuple

if os.environ.get('__USE_CUDA', '0') == '1':
    import cupy as np
    from cupy import ndarray, float64
    from cupyx.scipy.interpolate import make_interp_spline
    from cupyx.scipy.interpolate import BSpline
else:
    import numpy as np
    from numpy import ndarray, float64
    from scipy.interpolate import make_interp_spline
    from scipy.interpolate import BSpline

from g3d.defs import G3DWorld
from g3d.defs import AngleType
from g3d.defs import Axis
from g3d.defs import Direction
from g3d.defs import Position
from g3d.defs import NodeDistribution
from g3d.defs import FunctionByPass

from g3d.mesh.geo import Coordinates
from g3d.mesh.geo import Coord

from g3d.mesh.tools import rotate


[docs] class Edge(Coordinates): def __init__(self, *args: Any, **kwargs: Any) -> None: self.__nodes_distributed = False self.__start = Coord(tag='start') self.__end = Coord(tag='end') self.__origo = Coord(tag='origo') self._distribute = NodeDistribution.UNDEFINED self._stretch_rate = 'default' self._ialt = AngleType.UNDEFINED self._alpha = 0. self._beta = 0. self._direction = Direction.UNDEFINED self._axis = Axis.UNDEFINED self._data = None self._angle_dim = AngleType.DEGREES self._tolerance = 1.e-10 self._applied = False self._i0 = 0 self._j0 = 0 self._k0 = 0 self._idir = 0 self._npi = 0 super().__init__(*args, **kwargs) self.__init_Edge() def _set_default(self) -> None: self.__reset_def() super()._set_default() self._tag = 'g3d_edge' def _set_from_args(self, *args: Any, **kwargs: Any) -> ndarray: bps = self._get_function_evaluate_status(**kwargs) if bps is FunctionByPass.SELF: args_stat = np.ones(len(args)) else: args_stat = super()._set_from_args(*args, bps=FunctionByPass.BYPASS) if bps in [FunctionByPass.EVALUATE, FunctionByPass.SELF]: if np.sum(args_stat) > 0: n_int = 0 for arg in args: if type(arg) in [int, np.int64, np.int32]: n_int += 1 edge_def = [] start_coordinate_is_defined = False for i in range(len(args)): if args_stat[i] == 1: arg = args[i] if type(arg) in [int, np.int64, np.int32]: args_stat[i] = 0 if n_int == 1: if type(arg) in [np.int64, np.int32]: self._npi = arg.item() else: self._npi = arg else: edge_def.append(arg) elif type(arg) is Coord: args_stat[i] = 0 if start_coordinate_is_defined: self.__end = Coord(arg, copy_settings=True) else: self.__start = Coord(arg, copy_settings=True) start_coordinate_is_defined = True elif type(arg) is Edge: # if caught by a super class, the child class instance of copy will be used args_stat[i] = 0 self.copy(arg) self.__nodes_distributed = True elif type(arg) is list: args_stat[i] = 0 if self._list_is_all_ints(arg) and len(arg) == 5: self._i0, self._j0, self._k0, self._idir, self._npi = arg if len(edge_def) == 5: # will be caught by the for-loop below in a sub call to _set_from_args self._set_from_args(edge_def, bps=FunctionByPass.SELF) return args_stat def __reset_def(self) -> None: self._i0 = 0 self._j0 = 0 self._k0 = 0 self._idir = 0 self._npi = 0
[docs] def copy_settings(self, e: 'Edge') -> None: """ :param e: """ super().copy(e) self._distribute = e.distribute self._stretch_rate = e.stretch_rate self._ialt = e.ialt self._alpha = e.alpha self._beta = e.beta self._direction = e.direction self._axis = e.axis self._data = e.data self._angle_dim = e.angle_dim self._tolerance = e.tolerance
def __init_Edge(self) -> None: if not self.__nodes_distributed: if self._size != self._npi: self.allocate_XYZ() self.distribute_nodes()
[docs] def set_from_key_value(self, key: str, value: Any) -> bool: if super().set_from_key_value(key, value): return True elif key == 'start': self.__start = Coord(value, copy_settings=True) return True elif key == 'end': self.__end = Coord(value, copy_settings=True) return True elif key == 'origo': self.__origo = Coord(value, copy_settings=True) return True elif key == 'distribute': self._distribute = value return True elif key == 'stretch_rate': self._stretch_rate = value return True elif key == 'data': self._data = value return True elif key == 'direction': self._direction = value return True elif key == 'axis': self._axis = value return True elif key == 'angle_dim': self._angle_dim = value return True elif key == 'ialt': self._ialt = value return True elif key == 'alpha': self._alpha = value return True elif key == 'beta': self._beta = value return True elif key == 'x_shift': self.shift(x_shift=value) return True elif key == 'y_shift': self.shift(y_shift=value) return True elif key == 'z_shift': self.shift(z_shift=value) return True elif key == 'x': self.set_coordinates(x=value) return True elif key == 'y': self.set_coordinates(y=value) return True elif key == 'z': self.set_coordinates(z=value) return True elif key == 'tolerance': self._tolerance = value return True return False
[docs] def allocate_XYZ(self, N: int = None) -> None: self._size = self._npi super().allocate_XYZ(N)
[docs] def define_Edge(self, e: Union[list[int], int]) -> None: """ :param e: """ if type(e) is list: if len(e) == 5: self._i0, self._j0, self._k0, self._idir, self._npi = e elif type(e) is int: self._npi = e self._i0 = self._j0 = self._k0 = self._idir = 0 if self._size != self._npi: self.allocate_XYZ()
@property def applied(self) -> bool: """ :return: """ return self._applied @applied.setter def applied(self, b: bool) -> None: self._applied = b @property def i0(self) -> int: """ :return: """ return self._i0 @i0.setter def i0(self, i_val: int) -> None: self._i0 = i_val @property def j0(self) -> int: """ :return: """ return self._j0 @j0.setter def j0(self, i_val: int) -> None: self._j0 = i_val @property def k0(self) -> int: """ :return: """ return self._k0 @k0.setter def k0(self, i_val: int) -> None: self._k0 = i_val @property def idir(self) -> int: """ :return: """ return self._idir @idir.setter def idir(self, i_val: int) -> None: self._idir = i_val @property def npi(self) -> int: """ :return: """ return self._npi @npi.setter def npi(self, i_val: int) -> None: self._npi = i_val @property def start(self) -> Coord: """ :return: """ return self.__start @property def end(self) -> Coord: """ :return: """ return self.__end @property def stretch_rate(self) -> Union[float, str]: """ :return: """ return self._stretch_rate @stretch_rate.setter def stretch_rate(self, val: Union[float, str]) -> None: self._stretch_rate = val @property def direction(self) -> Any: """ :return: """ return self._direction @direction.setter def direction(self, val: Any) -> None: if type(val) is Direction: self._direction = val @property def axis(self) -> Any: """ :return: """ return self._axis @axis.setter def axis(self, val: Any) -> None: if type(val) is Axis: self._axis = val @property def angle_dim(self) -> Any: """ :return: """ return self._angle_dim @angle_dim.setter def angle_dim(self, val: Any) -> None: if type(val) is AngleType: self._angle_dim = val @property def ialt(self) -> Any: """ :return: """ return self._ialt @ialt.setter def ialt(self, val: Any) -> None: if type(val) is AngleType: self._ialt = val @property def alpha(self) -> float: """ :return: """ return self._alpha @alpha.setter def alpha(self, val: float) -> None: self._alpha = val @property def beta(self) -> float: """ :return: """ return self._beta @beta.setter def beta(self, val: float) -> None: self._beta = val @property def distribute(self) -> Any: """ :return: """ return self._distribute @distribute.setter def distribute(self, val: Any) -> None: if type(val) is NodeDistribution: self._distribute = val @property def data(self) -> Any: """ :return: """ return self._data @data.setter def data(self, val: Any) -> None: self._data = val @property def tolerance(self) -> float: """ :return: """ return self._tolerance @tolerance.setter def tolerance(self, val: float) -> None: self._tolerance = val @property def h0(self) -> float: """ :return: """ return self.get_spacing(position=Position.START) @property def h1(self) -> float: """ :return: """ return self.get_spacing(position=Position.END)
[docs] def print(self) -> None: super().print() print('edge definition: [i0=%d, j0=%d, k0=%d, idir=%d, npi=%d]' % (self._i0, self._j0, self._k0, self._idir, self._npi)) print('edge start coordinate: ') self.__start.print() print('edge end coordinate: ') self.__end.print() print('settings: ') print(' angle dimension: %s' % self._angle_dim.name) print(' node distribution: %s' % self._distribute.name) if type(self._stretch_rate) is str: print(' stretch rate: %s' % self._stretch_rate) else: print(' stretch rate: %f' % float(self._stretch_rate)) if self._direction.name != Direction.UNDEFINED: print(' direction: %s' % self._direction.name) if self._data is not None: if len(self._data) != 0: print(' data: %s' % str(self._data.size))
[docs] def set_start_end_Coord(self, start: Union[Coord, list], end: Union[Coord, list]) -> None: """ :param start: :param end: """ self.set_start_Coord(start) self.set_end_Coord(end)
[docs] def set_start_Coord(self, start: Union[Coord, list]) -> None: """ :param start: """ if start is not None: self.__start = Coord(start, copy_settings=True) if len(self._X) != 0: self._X[0] = self.__start.x self._Y[0] = self.__start.y self._Z[0] = self.__start.z
[docs] def set_end_Coord(self, end: Union[Coord, list]) -> None: """ :param end: """ if end is not None: self.__end = Coord(end, copy_settings=True) if len(self._X) != 0: self._X[-1] = self.__end.x self._Y[-1] = self.__end.y self._Z[-1] = self.__end.z
[docs] def update_start_Coord(self) -> None: """ """ self.__start.x = self._X[0] self.__start.y = self._Y[0] self.__start.z = self._Z[0]
[docs] def update_end_Coord(self) -> None: """ """ self.__end.x = self._X[-1] self.__end.y = self._Y[-1] self.__end.z = self._Z[-1]
[docs] def distribute_nodes(self) -> None: """ """ if self._npi != 0 and self.__start is not None and self.__end is not None: if self._size != self._npi: self.allocate_XYZ() self._X[0] = self.__start.x self._Y[0] = self.__start.y self._Z[0] = self.__start.z self._X[-1] = self.__end.x self._Y[-1] = self.__end.y self._Z[-1] = self.__end.z self.__distribute_nodes() self.__nodes_distributed = True
[docs] def re_distribute_nodes(self, **kwargs: Any) -> None: """ :param kwargs: """ if self._npi != 0 and self.__start is not None and self.__end is not None: self.set(**kwargs) if self._distribute == NodeDistribution.CURVE_1: self._data = np.zeros(shape=(self._npi, 3)) for i in range(self._npi): self._data[i, 0] = self._X[i] self._data[i, 1] = self._Y[i] self._data[i, 2] = self._Z[i] self.__distribute_nodes()
[docs] def copy(self, e: 'Edge') -> None: if e.start is not None: if self.__start is not None: self.__start.copy(e.start, copy_settings=True) else: self.__start = Coord(e.start, copy_settings=True) if e.end is not None: if self.__end is not None: self.__end.copy(e.end, copy_settings=True) else: self.__end = Coord(e.end, copy_settings=True) self.copy_settings(e) self.copy_def(e) self.copy_XYZ(e) self.__nodes_distributed = True
[docs] def copy_def(self, e: 'Edge') -> None: """ :param e: """ self._i0, self._j0, self._k0, self._idir, self._npi = e.get_def()
[docs] def get_def(self) -> Tuple[int, int, int, int, int]: """ :return: """ return self._i0, self._j0, self._k0, self._idir, self._npi
[docs] def get_Coord(self, lp: int) -> Coord: """ :param lp: :return: """ x, y, z = self.get_XYZ(lp) c = Coord(x, y, z) return c
[docs] def append(self, e: 'Edge', **kwargs: Any) -> None: """ :param e: :param kwargs: """ include_start_node = False for key, value in kwargs.items(): if key == 'include_start_node': include_start_node = value npi_tmp = self._npi X_tmp = np.zeros(npi_tmp) Y_tmp = np.zeros(npi_tmp) Z_tmp = np.zeros(npi_tmp) for i in range(npi_tmp): X_tmp[i] = self._X[i] Y_tmp[i] = self._Y[i] Z_tmp[i] = self._Z[i] self._npi += e.npi - 1 if include_start_node: self._npi += 1 self.allocate_XYZ() for i in range(npi_tmp): self._X[i] = X_tmp[i] self._Y[i] = Y_tmp[i] self._Z[i] = Z_tmp[i] i1 = 1 if include_start_node: i1 = 0 X_tmp, Y_tmp, Z_tmp = e.get_XYZ() for i in range(i1, e.npi): self._X[i + npi_tmp - i1] = X_tmp[i] self._Y[i + npi_tmp - i1] = Y_tmp[i] self._Z[i + npi_tmp - i1] = Z_tmp[i] self.update_end_Coord()
[docs] def prepend(self, e: 'Edge', **kwargs: Any) -> None: """ :param e: :param kwargs: """ include_end_node = False for key, value in kwargs.items(): if key == 'include_end_node': include_end_node = value npi_tmp = self._npi X_tmp = np.zeros(npi_tmp) Y_tmp = np.zeros(npi_tmp) Z_tmp = np.zeros(npi_tmp) for i in range(npi_tmp): X_tmp[i] = self._X[i] Y_tmp[i] = self._Y[i] Z_tmp[i] = self._Z[i] self._npi += e.npi - 1 if include_end_node: self._npi += 1 self.allocate_XYZ() i2 = e.npi - 1 if include_end_node: i2 += 1 for i in range(npi_tmp): self._X[i + i2] = X_tmp[i] self._Y[i + i2] = Y_tmp[i] self._Z[i + i2] = Z_tmp[i] X_tmp, Y_tmp, Z_tmp = e.get_XYZ() for i in range(i2): self._X[i] = X_tmp[i] self._Y[i] = Y_tmp[i] self._Z[i] = Z_tmp[i] self.update_start_Coord()
[docs] def get_sub(self, **kwargs: Any) -> 'Edge': """ :param kwargs: :return: """ i1 = 0 i2 = self._npi for key, value in kwargs.items(): if key == 'first': i1 = value - 1 elif key == 'last': i2 = value n = i2 - i1 e = Edge(n) X_sub, Y_sub, Z_sub = e.get_XYZ() for i in range(i1, i2): X_sub[i - i1] = self._X[i] Y_sub[i - i1] = self._Y[i] Z_sub[i - i1] = self._Z[i] e.update_start_Coord() e.update_end_Coord() return e
[docs] def generate_offset_Edge(self, offset: float, in_plane_vector: ndarray = np.array([0, 0, 1])) -> 'Edge': """ :param offset: :param in_plane_vector: :return: """ x3 = in_plane_vector[0] y3 = in_plane_vector[1] z3 = in_plane_vector[2] nx = np.zeros(self._npi) ny = np.zeros(self._npi) nz = np.zeros(self._npi) e = Edge(self) X_offset, Y_offset, Z_offset = e.get_XYZ() for i in range(self._npi - 1): x1 = self._X[i] x2 = self._X[i + 1] y1 = self._Y[i] y2 = self._Y[i + 1] z1 = self._Z[i] z2 = self._Z[i + 1] nx[i] += ((z2 - z1) * y3 - (y2 - y1) * z3) ny[i] += ((x2 - x1) * z3 - (z2 - z1) * x3) nz[i] += ((y2 - y1) * x3 - (x2 - x1) * y3) nx[i + 1] += ((y1 - y2) * z3 - (z1 - z2) * y3) ny[i + 1] += ((z1 - z2) * x3 - (x1 - x2) * z3) nz[i + 1] += ((x1 - x2) * y3 - (y1 - y2) * x3) for i in range(self._npi): r = np.sqrt(nx[i] ** 2 + ny[i] ** 2 + nz[i] ** 2) r = max(r, 1.e-20) X_offset[i] = self._X[i] + offset * nx[i] / r Y_offset[i] = self._Y[i] + offset * ny[i] / r Z_offset[i] = self._Z[i] + offset * nz[i] / r e.set_start_Coord([X_offset[0], Y_offset[0], Z_offset[0]]) e.set_end_Coord([X_offset[-1], Y_offset[-1], Z_offset[-1]]) return e
[docs] def rotate(self, **kwargs: Any) -> None: """ :param kwargs: :return: """ alpha = 0. beta = 0. gamma = 0. angle_dim = AngleType.DEGREES if len(kwargs) != 0: g3d_world = G3DWorld() for key, value in kwargs.items(): if g3d_world.display.verbose: print('%s == %s' % (key, str(value))) if key == 'alpha': alpha = value elif key == 'beta': beta = value elif key == 'gamma': gamma = value elif key == 'angle_dim': angle_dim = value else: print('warning! parameter %s is not defined' % key) if alpha == 0. and beta == 0. and gamma == 0.: print('warning! no rotation angle specified') return if angle_dim == AngleType.DEGREES: alpha *= np.pi / 180. beta *= np.pi / 180. gamma *= np.pi / 180. eps = 1.e-6 csa = np.cos(alpha) ssa = np.sin(alpha) csb = np.cos(beta) ssb = np.sin(beta) csg = np.cos(gamma) ssg = np.sin(gamma) for ip in range(self._npi): if np.abs(alpha) > eps: yy = self._Y[ip] zz = self._Z[ip] self._Y[ip] = csa * yy - ssa * zz self._Z[ip] = ssa * yy + csa * zz if np.abs(beta) > eps: xx = self._X[ip] zz = self._Z[ip] self._Z[ip] = csb * zz + ssb * xx self._X[ip] = -ssb * zz + csb * xx if np.abs(gamma) > eps: xx = self._X[ip] yy = self._Y[ip] self._X[ip] = csg * xx - ssg * yy self._Y[ip] = ssg * xx + csg * yy
[docs] def set_coordinates(self, **kwargs: Any) -> None: """ :param kwargs: """ if len(kwargs) != 0: g3d_world = G3DWorld() for key, value in kwargs.items(): if g3d_world.display.verbose: print('%s == %s' % (key, str(value))) if key == 'x': for i in range(self._npi): self._X[i] = value elif key == 'y': for i in range(self._npi): self._Y[i] = value elif key == 'z': for i in range(self._npi): self._Z[i] = value else: print('warning! parameter %s is not defined' % key)
[docs] def shift(self, **kwargs: Any) -> None: """ :param kwargs: :return: """ x_shift = 0. y_shift = 0. z_shift = 0. if len(kwargs) != 0: g3d_world = G3DWorld() for key, value in kwargs.items(): if g3d_world.display.verbose: print('%s == %s' % (key, str(value))) if key == 'x_shift': x_shift = value elif key == 'y_shift': y_shift = value elif key == 'z_shift': z_shift = value else: print('warning! parameter %s is not defined' % key) if x_shift == 0. and y_shift == 0. and z_shift == 0.: print('warning! no shift specified') return for i in range(self._npi): self._X[i] += x_shift self._Y[i] += y_shift self._Z[i] += z_shift self.__start.shift(x_shift=x_shift, y_shift=y_shift, z_shift=z_shift) self.__end.shift(x_shift=x_shift, y_shift=y_shift, z_shift=z_shift)
[docs] def get_spacing(self, **kwargs: Any) -> Union[float, float64]: """ :param kwargs: :return: """ position = Position.START if len(kwargs) != 0: g3d_world = G3DWorld() for key, value in kwargs.items(): if g3d_world.display.verbose: print('%s == %s' % (key, str(value))) if key == 'position': position = value else: print('warning! parameter %s is not defined' % key) dx = 0. dy = 0. dz = 0. if position == Position.START and len(self._X) > 1: dx = self._X[1] - self._X[0] dy = self._Y[1] - self._Y[0] dz = self._Z[1] - self._Z[0] if position == Position.END and len(self._X) > 1: dx = self._X[-1] - self._X[-2] dy = self._Y[-1] - self._Y[-2] dz = self._Z[-1] - self._Z[-2] return np.sqrt(dx ** 2 + dy ** 2 + dz ** 2)
[docs] def get_angle(self, **kwargs: Any) -> Union[None, Tuple[float, float]]: """ :param kwargs: :return: """ direction = Direction.START_TO_END if len(kwargs) != 0: g3d_world = G3DWorld() for key, value in kwargs.items(): if g3d_world.display.verbose: print('%s == %s' % (key, str(value))) if key == 'direction': direction = value if direction == Direction.START_TO_END or direction == Direction.POSITIVE: return self.__start.get_angle(self.__end, **kwargs) elif direction == Direction.END_TO_START or direction == Direction.NEGATIVE: return self.__end.get_angle(self.__start, **kwargs) elif direction == Direction.START_INWARD or direction == Direction.START_OUTWARD: if len(self._X) > 0: c_tmp = self.get_Coord(1) if direction == Direction.START_OUTWARD: return c_tmp.get_angle(self.__start, **kwargs) else: return self.__start.get_angle(c_tmp, **kwargs) elif direction == Direction.END_INWARD or direction == Direction.END_OUTWARD: if len(self._X) > 0 and self._npi >= 2: c_tmp = self.get_Coord(self._npi - 2) if direction == Direction.END_OUTWARD: return c_tmp.get_angle(self.__end, **kwargs) else: return self.__end.get_angle(c_tmp, **kwargs) return None
[docs] def get_tangent(self, lp: int, **kwargs: Any) -> Tuple[float, float]: """ :param lp: :param kwargs: :return: """ direction = Direction.POSITIVE if len(kwargs) != 0: g3d_world = G3DWorld() for key, value in kwargs.items(): if g3d_world.display.verbose: print('%s == %s' % (key, str(value))) if key == 'direction': direction = value lpm = max([lp - 1, 0]) lpp = min([lp + 1, self._npi - 1]) cm = self.get_Coord(lpm) cp = self.get_Coord(lpp) if direction == Direction.POSITIVE: return cm.get_angle(cp, **kwargs) else: return cp.get_angle(cm, **kwargs)
[docs] def find_index(self, x: float, **kwargs: Any) -> int: """ :param x: :param kwargs: :return: """ axis = Axis.X_AXIS for key, value in kwargs.items(): if key == 'axis': axis = value if axis == Axis.X_AXIS: return int(np.argmin(np.abs(self._X - x))) elif axis == Axis.Y_AXIS: return int(np.argmin(np.abs(self._Y - x))) else: # axis == Axis.Z_AXIS: return int(np.argmin(np.abs(self._Z - x)))
# =============================================================================== # NODE DISTRIBUTE FUNCTIONS # =============================================================================== def __distribute_nodes(self) -> None: self.allocate_XYZ() distribute = self._distribute if distribute == NodeDistribution.UNDEFINED: return elif distribute == NodeDistribution.ARC_1: self.__arc1() return elif distribute == NodeDistribution.CURVE_1: self.__curve1() return elif distribute == NodeDistribution.CURVE_2: self.__curve2() return elif distribute == NodeDistribution.CURVE_3: self.__curve3() return elif distribute == NodeDistribution.CURVE_4: self.__curve4() return elif distribute == NodeDistribution.CURVE_5: self.__curve5() return elif distribute == NodeDistribution.CURVE_10: self.__curve10() return return def __arc1(self) -> None: V = np.array xo = V(self.__origo.get_array()) x0 = V(self.__start.get_array()) x1 = V(self.__end.get_array()) npi = self._npi axis = self._axis if type(axis) is int: axis = Axis(axis) elif axis == Axis.UNDEFINED: axis = Axis.X_AXIS rad = self._alpha if self._angle_dim == AngleType.DEGREES and rad != 0.: rad *= np.pi / 180. if axis == Axis.X_AXIS: org = V([xo[2], xo[1]]) pnt1 = V([x0[2], x0[1]]) pnt2 = V([x1[2], x1[1]]) a = float(np.linalg.norm(org - pnt2)) b = float(np.linalg.norm(org - pnt1)) c = float(np.linalg.norm(pnt1 - pnt2)) if rad == 0.: rad = np.arccos((c ** 2. - a ** 2. - b ** 2.) / (2. * a * b)) angles = np.linspace(0, rad, npi) self._Z, self._Y = rotate(org, pnt1, angles) self._X = V([x0[0]] * npi) elif axis == Axis.Y_AXIS: org = V([xo[0], xo[2]]) pnt1 = V([x0[0], x0[2]]) pnt2 = V([x1[0], x1[2]]) a = float(np.linalg.norm(org - pnt2)) b = float(np.linalg.norm(org - pnt1)) c = float(np.linalg.norm(pnt1 - pnt2)) if rad == 0.: rad = np.arccos((c ** 2. - a ** 2. - b ** 2.) / (2. * a * b)) angles = np.linspace(0, rad, npi) self._X, self._Z = rotate(org, pnt1, angles) self._Y = V([x0[1]] * npi) else: org = V([xo[0], xo[1]]) pnt1 = V([x0[0], x0[1]]) pnt2 = V([x1[0], x1[1]]) a = float(np.linalg.norm(org - pnt2)) b = float(np.linalg.norm(org - pnt1)) c = float(np.linalg.norm(pnt1 - pnt2)) if rad == 0.: rad = np.arccos((c ** 2. - a ** 2. - b ** 2.) / (2. * a * b)) angles = np.linspace(0, rad, npi) self._X, self._Y = rotate(org, pnt1, angles) self._Z = V([x0[2]] * npi) def __curve1(self) -> None: V = np.array x1 = V(self.__start.get_array()) x2 = V(self.__end.get_array()) npi = self._npi h1 = self.__start.spacing h2 = self.__end.spacing direction = self._direction alpha = self._stretch_rate XX = self._data tolerance = self._tolerance if alpha == 'default': alpha = 0. if type(direction) is int: direction = Direction(direction) elif direction == Direction.UNDEFINED: direction = Direction.X_DIRECTION # in case specified coordinates are outside of data set, # add additional data point using zero-order extrapolation xx, yy, zz = [], [], [] if direction == Direction.X_DIRECTION: if x1[0] < XX[0, 0]: xx.append(x1[0]) yy.append(XX[0, 1]) zz.append(XX[0, 2]) if direction == Direction.Y_DIRECTION: if x1[1] < XX[0, 1]: xx.append(XX[0, 0]) yy.append(x1[1]) zz.append(XX[0, 2]) if direction == Direction.Z_DIRECTION: if x1[2] < XX[0, 2]: xx.append(XX[0, 0]) yy.append(XX[0, 1]) zz.append(x1[2]) for i in range(len(XX[:, 0])): xx.append(XX[i, 0]) yy.append(XX[i, 1]) zz.append(XX[i, 2]) if direction == Direction.X_DIRECTION: if x2[0] > XX[-1, 0]: xx.append(x2[0]) yy.append(XX[-1, 1]) zz.append(XX[-1, 2]) if direction == Direction.Y_DIRECTION: if x2[1] > XX[-1, 1]: xx.append(XX[-1, 0]) yy.append(x2[1]) zz.append(XX[-1, 2]) if direction == Direction.Z_DIRECTION: if x2[2] > XX[-1, 2]: xx.append(XX[-1, 0]) yy.append(XX[-1, 0]) zz.append(x2[2]) N = len(xx) X = np.zeros(shape=(N, 3)) X[:, 0] = V(xx) X[:, 1] = V(yy) X[:, 2] = V(zz) s = np.zeros(N) for i in range(1, N): s[i] = s[i - 1] + float(np.linalg.norm(X[i, :] - X[i - 1, :])) bs_x = make_interp_spline(s, X[:, 0]) bs_y = make_interp_spline(s, X[:, 1]) bs_z = make_interp_spline(s, X[:, 2]) # find parameter value for the specified edge start coordinate s1 = 0 s1x = bs_x(s1) - x1[0] s1y = bs_y(s1) - x1[1] s1z = bs_z(s1) - x1[2] s1_diff = float(np.linalg.norm(np.array([s1x, s1y, s1z]))) if s1_diff > tolerance: if direction == Direction.X_DIRECTION: tck_x = bs_x.tsk (t, c, k) = (tck_x[0], tck_x[1] - x1[0], tck_x[2]) bs = BSpline(t, c, k) s1 = bs.roots() elif direction == Direction.Y_DIRECTION: tck_y = bs_y.tsk (t, c, k) = (tck_y[0], tck_y[1] - x1[1], tck_y[2]) bs = BSpline(t, c, k) s1 = bs.roots() elif direction == Direction.Z_DIRECTION: tck_z = bs_z.tsk (t, c, k) = (tck_z[0], tck_z[1] - x1[2], tck_z[2]) bs = BSpline(t, c, k) s1 = bs.roots() if len(s1) == 0: s1 = 0 elif len(s1) > 1: s1 = s1[0] s2 = s[-1] s2x = bs_x(s2) - x2[0] s2y = bs_y(s2) - x2[1] s2z = bs_z(s2) - x2[2] s2_diff = float(np.linalg.norm(np.array([s2x, s2y, s2z]))) if s2_diff > tolerance: if direction == Direction.X_DIRECTION: tck_x = bs_x.tsk (t, c, k) = (tck_x[0], tck_x[1] - x2[0], tck_x[2]) bs = BSpline(t, c, k) s2 = bs.roots() elif direction == Direction.Y_DIRECTION: tck_y = bs_y.tsk (t, c, k) = (tck_y[0], tck_y[1] - x2[1], tck_y[2]) bs = BSpline(t, c, k) s2 = bs.roots() elif direction == Direction.Z_DIRECTION: tck_z = bs_z.tsk (t, c, k) = (tck_z[0], tck_z[1] - x2[2], tck_z[2]) bs = BSpline(t, c, k) s2 = bs.roots() if len(s2) == 0: s2 = s[-1] elif len(s2) > 1: s2 = s2[-1] if h1 < 0.: h1 = -h1 * (s2 - s1) / (npi - 1) if h2 < 0.: h2 = -h2 * (s2 - s1) / (npi - 1) ds1 = h1 * (npi - 1) ds2 = h2 * (npi - 1) self._X = np.zeros(npi) self._Y = np.zeros(npi) self._Z = np.zeros(npi) for i in range(npi): xsi = i / (npi - 1) # sp = 1. if alpha == 0.: g10 = 1. + (2. * xsi - 3.) * xsi * xsi g11 = (1. + (xsi - 2.) * xsi) * xsi g20 = (3. - 2. * xsi) * xsi * xsi g21 = (xsi - 1.) * xsi * xsi sp = g10 * s1 + g11 * ds1 + g20 * s2 + g21 * ds2 else: # gf = 1. if xsi < 0.5: gf = 0.5 * (np.exp(2. * alpha * xsi) - 1. - 2. * alpha * xsi) / (np.exp(alpha) - 1. - alpha) else: gf = 1. - 0.5 * ((np.exp(2. * alpha * (1. - xsi)) - 1. - 2. * alpha * (1. - xsi)) / (np.exp(alpha) - 1. - alpha)) g10 = 1. - gf g20 = gf g11 = xsi * g10 g21 = (xsi - 1.) * g20 sp = g10 * s1 + g11 * ds1 + g20 * s2 + g21 * ds2 self._X[i] = bs_x(sp) self._Y[i] = bs_y(sp) self._Z[i] = bs_z(sp) self._X[0] = x1[0] self._Y[0] = x1[1] self._Z[0] = x1[2] self._X[-1] = x2[0] self._Y[-1] = x2[1] self._Z[-1] = x2[2] def __curve2(self) -> None: V = np.array x0 = V(self.__start.get_array()) x1 = V(self.__end.get_array()) npi = self._npi h1 = self.__start.spacing h2 = self.__end.spacing alpha = self._stretch_rate if alpha == 'default': alpha = 0. if h1 == -1: h1 = float(np.linalg.norm(x1 - x0)) / (npi - 1) if h2 == -1: h2 = float(np.linalg.norm(x1 - x0)) / (npi - 1) dt1 = h1 / float(np.linalg.norm(x1 - x0)) * (npi - 1) dt2 = h2 / float(np.linalg.norm(x1 - x0)) * (npi - 1) self._X = np.zeros(npi) self._Y = np.zeros(npi) self._Z = np.zeros(npi) for i in range(npi): xsi = i / (npi - 1) if alpha > 0.: if xsi < 0.5: GF = (0.5 * (np.exp(2.0 * alpha * xsi) - 1.0 - 2.0 * alpha * xsi) / (np.exp(alpha) - 1.0 - alpha)) else: GF = (1.0 - 0.5 * (np.exp(2.0 * alpha * (1.0 - xsi)) - 1.0 - 2.0 * alpha * (1.0 - xsi)) / (np.exp(alpha) - 1.0 - alpha)) G20 = GF G11 = xsi * (1.0 - GF) G21 = (xsi - 1.0) * GF else: G11 = xsi - 2.0 * np.square(xsi) + np.power(xsi, 3.0) G20 = 3.0 * np.square(xsi) - 2.0 * np.power(xsi, 3.0) G21 = -np.square(xsi) + np.power(xsi, 3.0) T = G11 * dt1 + G20 + G21 * dt2 self._X[i] = (1.0 - T) * x0[0] + T * x1[0] self._Y[i] = (1.0 - T) * x0[1] + T * x1[1] self._Z[i] = (1.0 - T) * x0[2] + T * x1[2] def __curve3(self) -> None: # ialt=IALT_1 # alpha: angle between vector and x-axis # beta: angle between projection of vector on yz-plane and y-axis (positive towards z-axis) # # ialt=IALT_2 # alpha: angle between vector and y-axis # beta: angle between projection of vector on xz-plane and z-axis (positive towards x-axis) # # ialt=IALT_3 # alpha: angle between vector and z-axis # beta: angle between projection of vector on xy-plane and x-axis (positive towards y-axis) V = np.array x1 = V(self.__start.get_array()) x2 = V(self.__end.get_array()) npi = self._npi ialt1 = self.__start.ialt a1 = self.__start.alpha b1 = self.__start.beta d1 = self.__start.shape_factor h1 = self.__start.spacing alpha = self._stretch_rate if alpha == 'default': alpha = 0. if type(ialt1) is int: ialt1 = AngleType(ialt1) elif ialt1 == AngleType.UNDEFINED: ialt1 = AngleType.IALT_1 if self.__start.angle_dim == AngleType.DEGREES: a1 *= np.pi / 180. b1 *= np.pi / 180. if d1 < 0.: d1 = np.abs(d1) * float(np.linalg.norm(x2 - x1)) dx1 = V([np.cos(a1), np.sin(a1) * np.cos(b1), np.sin(a1) * np.sin(b1)]) if ialt1 == AngleType.IALT_2: dx1 = V([np.sin(a1) * np.sin(b1), np.cos(a1), np.sin(a1) * np.cos(b1)]) elif ialt1 == AngleType.IALT_3: dx1 = V([np.sin(a1) * np.cos(b1), np.sin(a1) * np.sin(b1), np.cos(a1)]) dx1 *= d1 N = 50 X = np.zeros(shape=(N, 3)) for i in range(N): xsi = i / (N - 1) g10 = 1. - xsi * xsi g11 = (1. - xsi) * xsi g20 = xsi * xsi X[i, 0] = g10 * x1[0] + g11 * dx1[0] + g20 * x2[0] X[i, 1] = g10 * x1[1] + g11 * dx1[1] + g20 * x2[1] X[i, 2] = g10 * x1[2] + g11 * dx1[2] + g20 * x2[2] s = np.zeros(N) for i in range(1, N): s[i] = s[i - 1] + float(np.linalg.norm(X[i, :] - X[i - 1, :])) bs_x = make_interp_spline(s, X[:, 0]) bs_y = make_interp_spline(s, X[:, 1]) bs_z = make_interp_spline(s, X[:, 2]) if h1 < 0.: h1 = -h1 * s[-1] / (npi - 1) ds1 = h1 * (npi - 1) self._X = np.zeros(npi) self._Y = np.zeros(npi) self._Z = np.zeros(npi) for i in range(npi): xsi = i / (npi - 1) gf = (np.exp(alpha * xsi) - 1. - alpha * xsi) / (np.exp(alpha) - 1. - alpha) g10 = 1. - gf g11 = xsi - gf g20 = gf sp = g10 * s[0] + g11 * ds1 + g20 * s[-1] self._X[i] = bs_x(sp) self._Y[i] = bs_y(sp) self._Z[i] = bs_z(sp) def __curve4(self) -> None: # ialt=IALT_1 # alpha: angle between vector and x-axis # beta: angle between projection of vector on yz-plane and y-axis (positive towards z-axis) # # ialt=IALT_2 # alpha: angle between vector and y-axis # beta: angle between projection of vector on xz-plane and z-axis (positive towards x-axis) # # ialt=IALT_3 # alpha: angle between vector and z-axis # beta: angle between projection of vector on xy-plane and x-axis (positive towards y-axis) V = np.array x1 = V(self.__start.get_array()) x2 = V(self.__end.get_array()) npi = self._npi ialt1 = self.__start.ialt a1 = self.__start.alpha b1 = self.__start.beta d1 = self.__start.shape_factor h1 = self.__start.spacing ialt2 = self.__end.ialt a2 = self.__end.alpha b2 = self.__end.beta d2 = self.__end.shape_factor h2 = self.__end.spacing alpha = self._stretch_rate if alpha == 'default': alpha = 0. if type(ialt1) is int: ialt1 = AngleType(ialt1) elif ialt1 == AngleType.UNDEFINED: ialt1 = AngleType.IALT_1 if type(ialt2) is int: ialt2 = AngleType(ialt2) elif ialt2 == AngleType.UNDEFINED: ialt2 = AngleType.IALT_1 if self.__start.angle_dim == AngleType.DEGREES: a1 *= np.pi / 180. b1 *= np.pi / 180. if self.__end.angle_dim == AngleType.DEGREES: a2 *= np.pi / 180. b2 *= np.pi / 180. if d1 < 0.: d1 = np.abs(d1) * float(np.linalg.norm(x2 - x1)) if d2 < 0.: d2 = np.abs(d2) * float(np.linalg.norm(x2 - x1)) dx1 = V([np.cos(a1), np.sin(a1) * np.cos(b1), np.sin(a1) * np.sin(b1)]) if ialt1 == AngleType.IALT_2: dx1 = V([np.sin(a1) * np.sin(b1), np.cos(a1), np.sin(a1) * np.cos(b1)]) elif ialt1 == AngleType.IALT_3: dx1 = V([np.sin(a1) * np.cos(b1), np.sin(a1) * np.sin(b1), np.cos(a1)]) dx1 *= d1 dx2 = V([np.cos(a2), np.sin(a2) * np.cos(b2), np.sin(a2) * np.sin(b2)]) if ialt2 == AngleType.IALT_2: dx2 = V([np.sin(a2) * np.sin(b2), np.cos(a2), np.sin(a2) * np.cos(b2)]) elif ialt2 == AngleType.IALT_3: dx2 = V([np.sin(a2) * np.cos(b2), np.sin(a2) * np.sin(b2), np.cos(a2)]) dx2 *= d2 N = 50 X = np.zeros(shape=(N, 3)) for i in range(N): xsi = i / (N - 1) g10 = 1. + (2. * xsi - 3.) * xsi * xsi g11 = (1. + (xsi - 2.) * xsi) * xsi g20 = (3. - 2. * xsi) * xsi * xsi g21 = (xsi - 1.) * xsi * xsi X[i, 0] = g10 * x1[0] + g11 * dx1[0] + g20 * x2[0] + g21 * dx2[0] X[i, 1] = g10 * x1[1] + g11 * dx1[1] + g20 * x2[1] + g21 * dx2[1] X[i, 2] = g10 * x1[2] + g11 * dx1[2] + g20 * x2[2] + g21 * dx2[2] s = np.zeros(N) for i in range(1, N): s[i] = s[i - 1] + float(np.linalg.norm(X[i, :] - X[i - 1, :])) bs_x = make_interp_spline(s, X[:, 0]) bs_y = make_interp_spline(s, X[:, 1]) bs_z = make_interp_spline(s, X[:, 2]) if h1 < 0.: h1 = -h1 * s[-1] / (npi - 1) if h2 < 0.: h2 = -h2 * s[-1] / (npi - 1) ds1 = h1 * (npi - 1) ds2 = h2 * (npi - 1) self._X = np.zeros(npi) self._Y = np.zeros(npi) self._Z = np.zeros(npi) for i in range(npi): xsi = i / (npi - 1) # sp = 1. if alpha == 0.: g10 = 1. + (2. * xsi - 3.) * xsi * xsi g11 = (1. + (xsi - 2.) * xsi) * xsi g20 = (3. - 2. * xsi) * xsi * xsi g21 = (xsi - 1.) * xsi * xsi sp = g10 * s[0] + g11 * ds1 + g20 * s[-1] + g21 * ds2 else: # gf = 1. if xsi < 0.5: gf = 0.5 * (np.exp(2. * alpha * xsi) - 1. - 2. * alpha * xsi) / (np.exp(alpha) - 1. - alpha) else: gf = 1. - 0.5 * ((np.exp(2. * alpha * (1. - xsi)) - 1. - 2. * alpha * (1. - xsi)) / (np.exp(alpha) - 1. - alpha)) g10 = 1. - gf g20 = gf g11 = xsi * g10 g21 = (xsi - 1.) * g20 sp = g10 * s[0] + g11 * ds1 + g20 * s[-1] + g21 * ds2 self._X[i] = bs_x(sp) self._Y[i] = bs_y(sp) self._Z[i] = bs_z(sp) def __curve5(self) -> None: V = np.array x1 = V(self.__start.get_array()) x2 = V(self.__end.get_array()) npi = self._npi h1 = self.__start.spacing alpha = self._stretch_rate if alpha == 'default': alpha = 0. N = 50 X = np.zeros(shape=(N, 3)) for i in range(N): xsi = i / (N - 1) g10 = 1. - xsi * xsi # g11 = (1. - xsi) * xsi g20 = xsi * xsi X[i, 0] = g10 * x1[0] + g20 * x2[0] X[i, 1] = g10 * x1[1] + g20 * x2[1] X[i, 2] = g10 * x1[2] + g20 * x2[2] s = np.zeros(N) for i in range(1, N): s[i] = s[i - 1] + float(np.linalg.norm(X[i, :] - X[i - 1, :])) bs_x = make_interp_spline(s, X[:, 0]) bs_y = make_interp_spline(s, X[:, 1]) bs_z = make_interp_spline(s, X[:, 2]) if h1 < 0.: h1 = -h1 * s[-1] / (npi - 1) ds1 = h1 * (npi - 1) self._X = np.zeros(npi) self._Y = np.zeros(npi) self._Z = np.zeros(npi) for i in range(npi): xsi = i / (npi - 1) gf = (np.exp(alpha * xsi) - 1. - alpha * xsi) / (np.exp(alpha) - 1. - alpha) g10 = 1. - gf g11 = xsi - gf g20 = gf sp = g10 * s[0] + g11 * ds1 + g20 * s[-1] self._X[i] = bs_x(sp) self._Y[i] = bs_y(sp) self._Z[i] = bs_z(sp) def __curve10(self) -> None: # ialt=IALT_1 # alpha: angle between vector and x-axis # beta: angle between projection of vector on yz-plane and y-axis (positive towards z-axis) # # ialt=IALT_2 # alpha: angle between vector and y-axis # beta: angle between projection of vector on xz-plane and z-axis (positive towards x-axis) # # ialt=IALT_3 # alpha: angle between vector and z-axis # beta: angle between projection of vector on xy-plane and x-axis (positive towards y-axis) npi = self._npi ialt = self._ialt alpha = self._alpha beta = self._beta if self._angle_dim == AngleType.DEGREES: alpha *= np.pi / 180. beta *= np.pi / 180. direction = Direction.UNDEFINED stretch_rate = self._stretch_rate XX = self._data start_rot = Coord(self.__start, copy_settings=True) end_rot = Coord(self.__end, copy_settings=True) cosa = np.cos(alpha) cosb = np.cos(beta) sina = np.sin(alpha) sinb = np.sin(beta) XX_rot = np.zeros(XX.shape) # rotate end coordinates and data set if ialt == AngleType.IALT_1: # alpha: angle between vector and x-axis # beta: angle between projection of vector on yz-plane and y-axis (positive towards z-axis) direction = Direction.X_DIRECTION x = self.__start.x y = self.__start.y z = self.__start.z start_rot.x = x * cosa + y * cosb * sina + z * sinb * sina start_rot.y = -x * sina + y * cosb * cosa + z * sinb * cosa start_rot.z = -y * sinb + z * cosb x = self.__end.x y = self.__end.y z = self.__end.z end_rot.x = x * cosa + y * cosb * sina + z * sinb * sina end_rot.y = -x * sina + y * cosb * cosa + z * sinb * cosa end_rot.z = -y * sinb + z * cosb for i in range(len(XX[:, 0])): x = XX[i, 0] y = XX[i, 1] z = XX[i, 2] XX_rot[i, 0] = x * cosa + y * cosb * sina + z * sinb * sina XX_rot[i, 1] = -x * sina + y * cosb * cosa + z * sinb * cosa XX_rot[i, 2] = -y * sinb + z * cosb elif ialt == AngleType.IALT_2: # alpha: angle between vector and y-axis # beta: angle between projection of vector on xz-plane and z-axis (positive towards x-axis) direction = Direction.Y_DIRECTION x = self.__start.x y = self.__start.y z = self.__start.z start_rot.x = x * cosb - z * sinb start_rot.y = x * sinb * sina + y * cosa + z * cosb * sina start_rot.z = x * sinb * cosa - y * sina + z * cosb * cosa x = self.__end.x y = self.__end.y z = self.__end.z end_rot.x = x * cosb - z * sinb end_rot.y = x * sinb * sina + y * cosa + z * cosb * sina end_rot.z = x * sinb * cosa - y * sina + z * cosb * cosa for i in range(len(XX[:, 0])): x = XX[i, 0] y = XX[i, 1] z = XX[i, 2] XX_rot[i, 0] = x * cosb - z * sinb XX_rot[i, 1] = x * sinb * sina + y * cosa + z * cosb * sina XX_rot[i, 2] = x * sinb * cosa - y * sina + z * cosb * cosa elif ialt == AngleType.IALT_3: # alpha: angle between vector and z-axis # beta: angle between projection of vector on xy-plane and x-axis (positive towards y-axis) direction = Direction.Z_DIRECTION x = self.__start.x y = self.__start.y z = self.__start.z start_rot.x = x * cosb * cosa + y * sinb * cosa - z * sina start_rot.y = -x * sinb + y * cosb start_rot.z = x * cosb * sina + y * sinb * sina + z * cosa x = self.__end.x y = self.__end.y z = self.__end.z end_rot.x = x * cosb * cosa + y * sinb * cosa - z * sina end_rot.y = -x * sinb + y * cosb end_rot.z = x * cosb * sina + y * sinb * sina + z * cosa for i in range(len(XX[:, 0])): x = XX[i, 0] y = XX[i, 1] z = XX[i, 2] XX_rot[i, 0] = x * cosb * cosa + y * sinb * cosa - z * sina XX_rot[i, 1] = -x * sinb + y * cosb XX_rot[i, 2] = x * cosb * sina + y * sinb * sina + z * cosa # distribute coordinates along edge in rotated coordinate system e = Edge(npi, start_rot, end_rot, distribute=NodeDistribution.CURVE_1, direction=direction, stretch_rate=stretch_rate, data=XX_rot) # rotate edge coordinates back to physical coordinate system xp_rot, yp_rot, zp_rot = e.get_XYZ() self._X = np.zeros(npi) self._Y = np.zeros(npi) self._Z = np.zeros(npi) if ialt == AngleType.IALT_1: for i in range(npi): x = xp_rot[i] y = yp_rot[i] z = zp_rot[i] self._X[i] = x * cosa - y * sina self._Y[i] = x * sina * cosb + y * cosa * cosb - z * sinb self._Z[i] = x * sina * sinb + y * cosa * sinb + z * cosb elif ialt == AngleType.IALT_2: for i in range(npi): x = xp_rot[i] y = yp_rot[i] z = zp_rot[i] self._X[i] = x * cosb + y * sina * sinb + z * cosa * sinb self._Y[i] = y * cosa - z * sina self._Z[i] = -x * sinb + y * sina * cosb + z * cosa * cosb elif ialt == AngleType.IALT_3: for i in range(npi): x = xp_rot[i] y = yp_rot[i] z = zp_rot[i] self._X[i] = x * cosa * cosb - y * sinb + z * sina * cosb self._Y[i] = x * cosa * sinb + y * cosb + z * sina * sinb self._Z[i] = -x * sina + z * cosa