diff --git a/gprMax/cmds_geometry/add_grass.py b/gprMax/cmds_geometry/add_grass.py index b159e078..a1f14c61 100644 --- a/gprMax/cmds_geometry/add_grass.py +++ b/gprMax/cmds_geometry/add_grass.py @@ -24,7 +24,7 @@ import numpy as np from ..fractals import FractalSurface, Grass from ..materials import DispersiveMaterial from ..utilities import round_value -from .cmds_geometry import UserObjectGeometry +from .cmds_geometry import UserObjectGeometry, rotate_2point_object logger = logging.getLogger(__name__) @@ -52,8 +52,7 @@ class AddGrass(UserObjectGeometry): def rotate(self, axis, angle, origin=None): pts = np.array([self.kwargs['p1'], self.kwargs['p2']]) - rotation = UserObjectGeometry.rotate_2point_object - rot_pts = rotation(self, pts, axis, angle, origin) + rot_pts = rotate_2point_object(pts, axis, angle, origin) self.kwargs['p1'] = tuple(rot_pts[0, :]) self.kwargs['p2'] = tuple(rot_pts[1, :]) diff --git a/gprMax/cmds_geometry/add_surface_roughness.py b/gprMax/cmds_geometry/add_surface_roughness.py index 4cf1fc2b..af7cab5c 100644 --- a/gprMax/cmds_geometry/add_surface_roughness.py +++ b/gprMax/cmds_geometry/add_surface_roughness.py @@ -22,7 +22,7 @@ import numpy as np from ..fractals import FractalSurface from ..utilities import round_value -from .cmds_geometry import UserObjectGeometry +from .cmds_geometry import UserObjectGeometry, rotate_2point_object logger = logging.getLogger(__name__) @@ -52,8 +52,7 @@ class AddSurfaceRoughness(UserObjectGeometry): def rotate(self, axis, angle, origin=None): pts = np.array([self.kwargs['p1'], self.kwargs['p2']]) - rotation = UserObjectGeometry.rotate_2point_object - rot_pts = rotation(self, pts, axis, angle, origin) + rot_pts = rotate_2point_object(pts, axis, angle, origin) self.kwargs['p1'] = tuple(rot_pts[0, :]) self.kwargs['p2'] = tuple(rot_pts[1, :]) diff --git a/gprMax/cmds_geometry/add_surface_water.py b/gprMax/cmds_geometry/add_surface_water.py index 4d4e0d80..74846432 100644 --- a/gprMax/cmds_geometry/add_surface_water.py +++ b/gprMax/cmds_geometry/add_surface_water.py @@ -23,7 +23,7 @@ import numpy as np from ..materials import DispersiveMaterial from ..utilities import round_value -from .cmds_geometry import UserObjectGeometry +from .cmds_geometry import UserObjectGeometry, rotate_2point_object logger = logging.getLogger(__name__) @@ -47,8 +47,7 @@ class AddSurfaceWater(UserObjectGeometry): def rotate(self, axis, angle, origin=None): pts = np.array([self.kwargs['p1'], self.kwargs['p2']]) - rotation = UserObjectGeometry.rotate_2point_object - rot_pts = rotation(self, pts, axis, angle, origin) + rot_pts = rotate_2point_object(pts, axis, angle, origin) self.kwargs['p1'] = tuple(rot_pts[0, :]) self.kwargs['p2'] = tuple(rot_pts[1, :]) diff --git a/gprMax/cmds_geometry/box.py b/gprMax/cmds_geometry/box.py index 136303cd..105e3e2f 100644 --- a/gprMax/cmds_geometry/box.py +++ b/gprMax/cmds_geometry/box.py @@ -22,7 +22,7 @@ import numpy as np from ..cython.geometry_primitives import build_box from ..materials import Material -from .cmds_geometry import UserObjectGeometry +from .cmds_geometry import UserObjectGeometry, rotate_2point_object logger = logging.getLogger(__name__) @@ -48,8 +48,7 @@ class Box(UserObjectGeometry): def rotate(self, axis, angle, origin=None): pts = np.array([self.kwargs['p1'], self.kwargs['p2']]) - rotation = UserObjectGeometry.rotate_2point_object - rot_pts = rotation(self, pts, axis, angle, origin) + rot_pts = rotate_2point_object(pts, axis, angle, origin) self.kwargs['p1'] = tuple(rot_pts[0, :]) self.kwargs['p2'] = tuple(rot_pts[1, :]) diff --git a/gprMax/cmds_geometry/cmds_geometry.py b/gprMax/cmds_geometry/cmds_geometry.py index d14f5555..df1e9a4a 100644 --- a/gprMax/cmds_geometry/cmds_geometry.py +++ b/gprMax/cmds_geometry/cmds_geometry.py @@ -44,95 +44,140 @@ class UserObjectGeometry: return f'{self.hash}: {s[:-1]}' def create(self, grid, uip): - """Create the object and add it to the grid.""" + """Create object and add it to the grid.""" pass def rotate(self, axis, angle, origin=None): """Rotate object - specialised for each object.""" pass - def rotate_point(self, p, axis, angle, origin=(0, 0, 0)): - """Rotate a point. + +def rotate_point(p, axis, angle, origin=(0, 0, 0)): + """Rotate a point. + + Args: + p (array): coordinates of point (x, y, z) + axis (str): axis about which to perform rotation (x, y, or z) + angle (int): angle of rotation (degrees) + origin (tuple): point about which to perform rotation (x, y, z) + + Returns: + p (array): coordinates of rotated point (x, y, z) + """ + + origin = np.array(origin) + + # Move point to axis of rotation + p -= origin + + # Calculate rotation matrix + r = R.from_euler(axis, angle, degrees=True) + + # Apply rotation + p = r.apply(p) + + # Move object back to original axis + p += origin + + return p - Args: - p (array): coordinates of point (x, y, z) - axis (str): axis about which to perform rotation (x, y, or z) - angle (int): angle of rotation (degrees) - origin (tuple): point about which to perform rotation (x, y, z) - Returns: - p (array): coordinates of rotated point (x, y, z) - """ +def rotate_2point_object(pts, axis, angle, origin=None): + """Rotate a geometry object that is defined by 2 points. + + Args: + pts (array): coordinates of points of object to be rotated + axis (str): axis about which to perform rotation (x, y, or z) + angle (int): angle of rotation (degrees) + origin (tuple): point about which to perform rotation (x, y, z) - origin = np.array(origin) + Returns: + new_pts (array): coordinates of points of rotated object + """ + + # Use origin at centre of object if not given + if not origin: + origin = pts[0,:] + (pts[1,:] - pts[0,:]) / 2 + + # Check angle value is suitable + angle = int(angle) + if angle < 0 or angle > 360: + logger.exception(self.__str__() + ' angle of rotation must be between 0-360 degrees') + raise ValueError + if angle % 90 != 0: + logger.exception(self.__str__() + ' angle of rotation must be a multiple of 90 degrees') + raise ValueError - # Move point to axis of rotation - p -= origin + # Check axis is valid + if axis != 'x' and axis != 'y' and axis != 'z': + logger.exception(self.__str__() + ' axis of rotation must be x, y, or z') + raise ValueError - # Calculate rotation matrix - r = R.from_euler(axis, angle, degrees=True) + # Save original points + orig_pts = pts - # Apply rotation - p = r.apply(p) + # Rotate points that define object + pts[0, :] = rotate_point(pts[0, :], axis, angle, origin) + pts[1, :] = rotate_point(pts[1, :], axis, angle, origin) - # Move object back to original axis - p += origin + # Get lower left and upper right coordinates to define new object + new_pts = np.zeros(pts.shape) + new_pts[0, :] = np.min(pts, axis=0) + new_pts[1, :] = np.max(pts, axis=0) - return p - - def rotate_2point_object(self, pts, axis, angle, origin=None): - """Rotate a geometry object that is defined by 2 points. - - Args: - pts (array): coordinates of points of object to be rotated - axis (str): axis about which to perform rotation (x, y, or z) - angle (int): angle of rotation (degrees) - origin (tuple): point about which to perform rotation (x, y, z) + # Reset coordinates of invariant direction + # - only needed for 2D models, has no effect on 3D models. + if axis =='x': + new_pts[0, 0] = orig_pts[0, 0] + new_pts[1, 0] = orig_pts[1, 0] + elif axis == 'y': + new_pts[0, 1] = orig_pts[0, 1] + new_pts[1, 1] = orig_pts[1, 1] + elif axis == 'z': + new_pts[0, 2] = orig_pts[0, 2] + new_pts[1, 2] = orig_pts[1, 2] - Returns: - new_pts (array): coordinates of points of rotated object - """ + return new_pts - # Use origin at centre of object if not given - if not origin: - origin = pts[0,:] + (pts[1,:] - pts[0,:]) / 2 - # Check angle value is suitable - angle = int(angle) - if angle < 0 or angle > 360: - logger.exception(self.__str__() + ' angle of rotation must be between 0-360 degrees') - raise ValueError - if angle % 90 != 0: - logger.exception(self.__str__() + ' angle of rotation must be a multiple of 90 degrees') - raise ValueError +def rotate_polarisation(p, polarisation, axis, angle): + """Rotate a geometry object that is defined by 2 points. + + Args: + p (array): coordinates of point (x, y, z) + polarisation (str): current polarisation (x, y, or z) + axis (str): axis about which to perform rotation (x, y, or z) + angle (int): angle of rotation (degrees) - # Check axis is valid - if axis != 'x' and axis != 'y' and axis != 'z': - logger.exception(self.__str__() + ' axis of rotation must be x, y, or z') - raise ValueError + Returns: + pts (array): coordinates of points of rotated object + new_polarisation (str): new polarisation (x, y, or z) + """ - # Save original points - orig_pts = pts + logger.debug('Need to get dxdydz into this function') + dxdydz = np.array([0.001, 0.001, 0.001]) - # Rotate points that define object - pts[0, :] = self.rotate_point(pts[0, :], axis, angle, origin) - pts[1, :] = self.rotate_point(pts[1, :], axis, angle, origin) + if polarisation.lower() == 'x': + new_pt = (p[0] + dxdydz[0], p[1], p[2]) + if axis == 'y' and angle == 90 or angle == 270: + new_polarisation = 'z' + if axis == 'z' and angle == 90 or angle == 270: + new_polarisation = 'y' - # Get lower left and upper right coordinates to define new object - new_pts = np.zeros(pts.shape) - new_pts[0, :] = np.min(pts, axis=0) - new_pts[1, :] = np.max(pts, axis=0) + elif polarisation.lower() == 'y': + new_pt = (p[0], p[1] + dxdydz[1], p[2]) + if axis == 'x' and angle == 90 or angle == 270: + new_polarisation = 'z' + if axis == 'z' and angle == 90 or angle == 270: + new_polarisation = 'x' - # Reset coordinates of invariant direction - # - only needed for 2D models, has no effect on 3D models. - if axis =='x': - new_pts[0, 0] = orig_pts[0, 0] - new_pts[1, 0] = orig_pts[1, 0] - elif axis == 'y': - new_pts[0, 1] = orig_pts[0, 1] - new_pts[1, 1] = orig_pts[1, 1] - elif axis == 'z': - new_pts[0, 2] = orig_pts[0, 2] - new_pts[1, 2] = orig_pts[1, 2] + elif polarisation.lower() == 'z': + new_pt = (p[0], p[1], p[2] + dxdydz[2]) + if axis == 'x' and angle == 90 or angle == 270: + new_polarisation = 'y' + if axis == 'y' and angle == 90 or angle == 270: + new_polarisation = 'x' - return new_pts + pts = np.array([p, new_pt]) + + return pts, new_polarisation \ No newline at end of file diff --git a/gprMax/cmds_geometry/edge.py b/gprMax/cmds_geometry/edge.py index b8d735e6..10e893b1 100644 --- a/gprMax/cmds_geometry/edge.py +++ b/gprMax/cmds_geometry/edge.py @@ -22,7 +22,7 @@ import numpy as np from ..cython.geometry_primitives import (build_edge_x, build_edge_y, build_edge_z) -from .cmds_geometry import UserObjectGeometry +from .cmds_geometry import UserObjectGeometry, rotate_2point_object logger = logging.getLogger(__name__) @@ -44,8 +44,7 @@ class Edge(UserObjectGeometry): def rotate(self, axis, angle, origin=None): pts = np.array([self.kwargs['p1'], self.kwargs['p2']]) - rotation = UserObjectGeometry.rotate_2point_object - rot_pts = rotation(self, pts, axis, angle, origin) + rot_pts = rotate_2point_object(pts, axis, angle, origin) self.kwargs['p1'] = tuple(rot_pts[0, :]) self.kwargs['p2'] = tuple(rot_pts[1, :]) diff --git a/gprMax/cmds_geometry/fractal_box.py b/gprMax/cmds_geometry/fractal_box.py index d2439d3a..8be92ab9 100644 --- a/gprMax/cmds_geometry/fractal_box.py +++ b/gprMax/cmds_geometry/fractal_box.py @@ -21,7 +21,7 @@ import logging import numpy as np from ..fractals import FractalVolume -from .cmds_geometry import UserObjectGeometry +from .cmds_geometry import UserObjectGeometry, rotate_2point_object logger = logging.getLogger(__name__) @@ -55,8 +55,7 @@ class FractalBox(UserObjectGeometry): def rotate(self, axis, angle, origin=None): pts = np.array([self.kwargs['p1'], self.kwargs['p2']]) - rotation = UserObjectGeometry.rotate_2point_object - rot_pts = rotation(self, pts, axis, angle, origin) + rot_pts = rotate_2point_object(pts, axis, angle, origin) self.kwargs['p1'] = tuple(rot_pts[0, :]) self.kwargs['p2'] = tuple(rot_pts[1, :]) diff --git a/gprMax/cmds_geometry/plate.py b/gprMax/cmds_geometry/plate.py index f4c71045..95eb2c1f 100644 --- a/gprMax/cmds_geometry/plate.py +++ b/gprMax/cmds_geometry/plate.py @@ -22,7 +22,7 @@ import numpy as np from ..cython.geometry_primitives import (build_face_xy, build_face_xz, build_face_yz) -from .cmds_geometry import UserObjectGeometry +from .cmds_geometry import UserObjectGeometry, rotate_2point_object logger = logging.getLogger(__name__) @@ -46,8 +46,7 @@ class Plate(UserObjectGeometry): def rotate(self, axis, angle, origin=None): pts = np.array([self.kwargs['p1'], self.kwargs['p2']]) - rotation = UserObjectGeometry.rotate_2point_object - rot_pts = rotation(self, pts, axis, angle, origin) + rot_pts = rotate_2point_object(pts, axis, angle, origin) self.kwargs['p1'] = tuple(rot_pts[0, :]) self.kwargs['p2'] = tuple(rot_pts[1, :]) diff --git a/gprMax/cmds_geometry/sphere.py b/gprMax/cmds_geometry/sphere.py index fb112ed8..7a126a11 100644 --- a/gprMax/cmds_geometry/sphere.py +++ b/gprMax/cmds_geometry/sphere.py @@ -46,9 +46,6 @@ class Sphere(UserObjectGeometry): super().__init__(**kwargs) self.hash = '#sphere' - def rotate(self, axis, angle, origin=None): - pass - def create(self, grid, uip): try: p1 = self.kwargs['p1'] diff --git a/gprMax/cmds_multiuse.py b/gprMax/cmds_multiuse.py index fa8d3011..c95bb2ef 100644 --- a/gprMax/cmds_multiuse.py +++ b/gprMax/cmds_multiuse.py @@ -18,10 +18,13 @@ import logging -import gprMax.config as config import numpy as np -from .cmds_geometry.cmds_geometry import UserObjectGeometry +import gprMax.config as config + +from .cmds_geometry.cmds_geometry import (UserObjectGeometry, + rotate_2point_object, + rotate_polarisation) from .geometry_outputs import GeometryObjects as GeometryObjectsUser from .materials import DispersiveMaterial as DispersiveMaterialUser from .materials import Material as MaterialUser @@ -48,7 +51,6 @@ class UserObjectMulti: self.order = None self.hash = '#example' self.autotranslate = True - self.rotate_point = UserObjectGeometry.rotate_point def __str__(self): """Readable user string as per hash commands.""" @@ -61,10 +63,11 @@ class UserObjectMulti: return f'{self.hash}: {s[:-1]}' def create(self, grid, uip): - """Create the object and add it to the grid.""" + """Create object and add it to the grid.""" pass def rotate(self, axis, angle, origin=None): + """Rotate object - specialised for each object.""" pass def params_str(self): @@ -142,50 +145,10 @@ class VoltageSource(UserObjectMulti): self.hash = '#voltage_source' def rotate(self, axis, angle, origin=None): - pts = np.array([self.kwargs['p1'], self.kwargs['p2']]) - dxdydz = (0.001, 0.001, 0.001) - if self.kwargs['polarisation'].lower() == 'x': - new_pt = (self.kwargs['p1'][0] + dxdydz[0], - self.kwargs['p1'][1], - self.kwargs['p1'][2]) - if axis == 'y' and angle == 90 or angle == 270: - self.kwargs['polarisation'] = 'z' - if axis == 'z' and angle == 90 or angle == 270: - self.kwargs['polarisation'] = 'y' - - pts = np.array([self.kwargs['p1'], new_pt]) - - rotation = UserObjectGeometry.rotate_2point_object - rot_pts = rotation(self, pts, axis, angle, origin) + rot_pol_pts, self.kwargs['polarisation'] = rotate_polarisation(self.kwargs['p1'], self.kwargs['polarisation'], axis, angle) + rot_pts = rotate_2point_object(rot_pol_pts, axis, angle, origin) self.kwargs['p1'] = tuple(rot_pts[0, :]) - - if axis == 'x': - p[0] = origp[0] - if self.kwargs['polarisation'].lower() == 'y': - if angle == 90 or angle == 270: - self.kwargs['polarisation'] = 'z' - elif self.kwargs['polarisation'].lower() =='z': - if angle == 90 or angle == 270: - self.kwargs['polarisation'] = 'y' - elif axis == 'y': - p[1] = origp[1] - if self.kwargs['polarisation'].lower() == 'x': - if angle == 90 or angle == 270: - self.kwargs['polarisation'] = 'z' - elif self.kwargs['polarisation'].lower() == 'z': - if angle == 90 or angle == 270: - self.kwargs['polarisation'] = 'x' - elif axis == 'z': - p[2] = origp[2] - if self.kwargs['polarisation'].lower() == 'x': - if angle == 90 or angle == 270: - self.kwargs['polarisation'] = 'y' - elif self.kwargs['polarisation'].lower() == 'y': - if angle == 90 or angle == 270: - self.kwargs['polarisation'] = 'x' - - def create(self, grid, uip): try: p1 = self.kwargs['p1'] @@ -282,6 +245,11 @@ class HertzianDipole(UserObjectMulti): self.order = 3 self.hash = '#hertzian_dipole' + def rotate(self, axis, angle, origin=None): + rot_pol_pts, self.kwargs['polarisation'] = rotate_polarisation(self.kwargs['p1'], self.kwargs['polarisation'], axis, angle) + rot_pts = rotate_2point_object(rot_pol_pts, axis, angle, origin) + self.kwargs['p1'] = tuple(rot_pts[0, :]) + def create(self, grid, uip): try: polarisation = self.kwargs['polarisation'].lower() @@ -387,6 +355,11 @@ class MagneticDipole(UserObjectMulti): self.order = 4 self.hash = '#magnetic_dipole' + def rotate(self, axis, angle, origin=None): + rot_pol_pts, self.kwargs['polarisation'] = rotate_polarisation(self.kwargs['p1'], self.kwargs['polarisation'], axis, angle) + rot_pts = rotate_2point_object(rot_pol_pts, axis, angle, origin) + self.kwargs['p1'] = tuple(rot_pts[0, :]) + def create(self, grid, uip): try: polarisation = self.kwargs['polarisation'].lower() @@ -482,6 +455,11 @@ class TransmissionLine(UserObjectMulti): self.order = 5 self.hash = '#transmission_line' + def rotate(self, axis, angle, origin=None): + rot_pol_pts, self.kwargs['polarisation'] = rotate_polarisation(self.kwargs['p1'], self.kwargs['polarisation'], axis, angle) + rot_pts = rotate_2point_object(rot_pol_pts, axis, angle, origin) + self.kwargs['p1'] = tuple(rot_pts[0, :]) + def create(self, grid, uip): try: polarisation = self.kwargs['polarisation'].lower() @@ -583,6 +561,24 @@ class Rx(UserObjectMulti): self.hash = '#rx' self.constructor = RxUser + def rotate(self, axis, angle, origin=None): + logger.debug('Need to get dxdydz into this function') + dxdydz = np.array([0.001, 0.001, 0.001]) + new_pt = (self.kwargs['p1'][0] + dxdydz[0], self.kwargs['p1'][1] + dxdydz[1], self.kwargs['p1'][2] + dxdydz[2]) + pts = np.array([self.kwargs['p1'], new_pt]) + rot_pts = rotate_2point_object(pts, axis, angle, origin) + self.kwargs['p1'] = tuple(rot_pts[0, :]) + + # If specific field components are specified, set to output all components + try: + ID = self.kwargs['id'] + outputs = [self.kwargs['outputs']] + rxargs = dict(self.kwargs) + del rxargs['outputs'] + self.kwargs = rxargs + except KeyError: + pass + def create(self, grid, uip): try: p1 = self.kwargs['p1']