Source code for empyre.fields.shapes

# -*- coding: utf-8 -*-
# Copyright 2020 by Forschungszentrum Juelich GmbH
# Author: J. Caron
#


import logging

import numpy as np

from .field import Field


__all__ = ['create_shape_slab', 'create_shape_disc', 'create_shape_ellipse', 'create_shape_ellipsoid',
           'create_shape_sphere', 'create_shape_filament', 'create_shape_voxel']

_log = logging.getLogger(__name__)


# TODO: TEST!!!


[docs]def create_shape_slab(dim, center=None, width=None, scale=1.0): """Creates a Field object with the shape of a slab as a scalar field in arbitrary dimensions. Attributes ---------- dim : tuple The dimensions of the grid. center : tuple, optional The center of the slab in pixel coordinates. width : tuple, optional The width of the slab in pixel coordinates. scale: tuple of float Scaling along the dimensions of the underlying data. """ _log.debug('Calling __init__') if center is None: center = tuple([d/2 for d in dim]) if width is None: width = tuple([d/2 for d in dim]) assert len(dim) == len(center) == len(width), 'Parameters dim, center and width must have the same dimensions!' data = np.zeros(dim) bounds = () for i in range(len(dim)): start = int(np.floor(center[i] - width[i]/2)) stop = int(np.ceil(center[i] + width[i]/2)) bounds += (slice(start, stop),) data[bounds] = 1 return Field(data=data, scale=scale, vector=False)
[docs]def create_shape_disc(dim, center=None, radius=None, height=None, axis=0, scale=1.0): """Creates a Field object with the shape of a cylindrical disc in 2D or 3D. Attributes ---------- dim : tuple The dimensions of the grid. center : tuple, optional The center of the disc in pixel coordinates. radius : float, optional The radius of the disc in pixel coordinates. height : float, optional The height of the disc in pixel coordinates. Unused if only 2D. axis : int, optional The orientation of the discs orthogonal axis. Only used in 3D case with z-axis as default. scale: tuple of float Scaling along the dimensions of the underlying data. """ _log.debug('Calling __init__') assert len(dim) in (2, 3), 'Disc can only be build in 2 or 3 dimensions!' # Find indices of the disc plane axes: idx_uv = [0, 1, 2] if len(dim) == 3: # 3D: idx_uv.remove(axis) else: # 2D: idx_uv.remove(2) # Find default values: if center is None: center = tuple([d/2 for d in dim]) if radius is None: radius = np.max((dim[idx_uv[0]], dim[idx_uv[1]])) / 4 if height is None and len(dim) == 3: # only used for 3D! height = dim[axis] / 2 assert height > 0, 'Height has to be a positive scalar value!' assert len(dim) == len(center), 'Parameters dim and center must have the same dimensions!' assert radius > 0, 'Radius has to be a positive scalar value!' coords = np.indices(dim) + 0.5 # 0.5 to get to pixel/voxel center! coords = coords - np.asarray(center)[(slice(None),) + (None,)*len(dim)] # [:, None, None, None] data = np.where(np.hypot(coords[idx_uv[0]], coords[idx_uv[1]]) <= radius, 1, 0) if len(dim) == 3: # 3D: Implement bounds above and below the disc: height_shape = np.zeros_like(data) bounds = [slice(None)] * 3 # i.e.: [:, :, :] start = int(np.floor(center[axis] - height/2)) stop = int(np.ceil(center[axis] + height/2)) bounds[axis] = slice(start, stop) # replace with actual bounds along disc symmetry axis height_shape[tuple(bounds)] = 1 data *= height_shape return Field(data=data, scale=scale, vector=False)
[docs]def create_shape_ellipse(dim, center=None, width=None, height=None, axis=0, scale=1.0): """Creates a Field object with the shape of an ellipse in 2D or 3D. Attributes ---------- dim : tuple The dimensions of the grid. center : tuple, optional The center of the ellipse in pixel coordinates. width : tuple, optional The two half axes of the ellipse in pixel coordinates. height : float, optional The height of the ellipse in pixel coordinates. Unused if only 2D. axis : int, optional The orientation of the ellipses orthogonal axis. Only used in 3D case with z-axis as default. scale: tuple of float Scaling along the dimensions of the underlying data. """ _log.debug('Calling __init__') assert len(dim) in (2, 3), 'Ellipse can only be build in 2 or 3 dimensions!' # Find indices of the disc plane axes: idx_uv = [0, 1, 2] if len(dim) == 3: # 3D: idx_uv.remove(axis) else: # 2D: idx_uv.remove(2) # Find default values: if center is None: center = tuple([d/2 for d in dim]) if width is None: dim_uv = (dim[idx_uv[0]], dim[idx_uv[1]]) width = (np.max(dim_uv)*1/3, np.min(dim_uv)*2/3) if height is None and len(dim) == 3: # only used for 3D! height = dim[axis] / 2 assert len(dim) == len(center), 'Parameters dim and center must have the same dimensions!' assert len(width) == 2, 'Width has to contain the two half axes!' coords = np.indices(dim) + 0.5 # 0.5 to get to pixel/voxel center! coords = coords - np.asarray(center)[(slice(None),) + (None,)*len(dim)] # [:, None, None, None] distance = np.hypot(coords[idx_uv[1]] / (width[1]/2), coords[idx_uv[0]] / (width[0]/2)) data = np.where(distance <= 1, 1, 0) if len(dim) == 3: # 3D: Implement bounds above and below the disc: height_shape = np.zeros_like(data) bounds = [slice(None)] * 3 # i.e.: [:, :, :] start = int(np.floor(center[axis] - height/2)) stop = int(np.ceil(center[axis] + height/2)) bounds[axis] = slice(start, stop) # replace with actual bounds along disc symmetry axis height_shape[tuple(bounds)] = 1 data *= height_shape return Field(data=data, scale=scale, vector=False)
[docs]def create_shape_ellipsoid(dim, center=None, width=None, scale=1.0): """Creates a Field object with the shape of an ellipsoid in arbitrary dimensions. Attributes ---------- dim : tuple The dimensions of the grid. center : tuple, optional The center of the ellipsoid in pixel coordinates. width : tuple, optional The half axes of the ellipsoid in pixel coordinates. scale: tuple of float Scaling along the dimensions of the underlying data. """ _log.debug('Calling __init__') if center is None: center = tuple([d/2 for d in dim]) if width is None: width = tuple([d/2 for d in dim]) assert len(dim) == len(center) == len(width), 'Parameters dim, center and width must have the same dimensions!' coords = np.indices(dim) + 0.5 # 0.5 to get to pixel/voxel center! coords = coords - np.asarray(center)[(slice(None),) + (None,)*len(dim)] # [:, None, None, None] distance = np.sqrt([(coords[i] / (width[i]/2))**2 for i in range(len(dim))]) data = np.where(distance <= 1, 1, 0) return Field(data=data, scale=scale, vector=False)
[docs]def create_shape_sphere(dim, center=None, radius=None, scale=1.0): """Creates a Field object with the shape of a sphere in arbitrary dimensions. Attributes ---------- dim : tuple The dimensions of the grid. center : tuple, optional The center of the sphere in pixel coordinates. width : tuple, optional The half axes of the sphere in pixel coordinates. scale: tuple of float Scaling along the dimensions of the underlying data. """ _log.debug('Calling __init__') if center is None: center = tuple([d/2 for d in dim]) if radius is None: radius = np.max(dim) / 4 assert len(dim) == len(center), 'Parameters dim and center must have the same dimensions!' assert radius > 0, 'Radius has to be a positive scalar value!' coords = np.indices(dim) + 0.5 # 0.5 to get to pixel/voxel center! coords = coords - np.asarray(center)[(slice(None),) + (None,)*len(dim)] # [:, None, None, None] distance = np.sqrt([coords[i]**2 for i in range(len(dim))]) data = np.where(distance <= radius, 1, 0) return Field(data=data, scale=scale, vector=False)
[docs]def create_shape_filament(dim, pos=None, axis=0, scale=1.0): """Creates a Field object with the shape of a filament in arbitrary dimension. Parameters ---------- dim : tuple The dimensions of the grid. pos : tuple, optional The position of the filament in pixel coordinates. Has to be a tuple of len(dim) - 1 and denotes the index positions along all axes aside from the specified `axis` along which the filament is placed. axis : int, optional The orientation of the filament axis. Defaults to the first axis. scale: tuple of float Scaling along the dimensions of the underlying data. """ _log.debug('Calling __init__') assert len(dim) > 1, 'Only usable for multidimensional Fields (at least 2 dimensions)!' if pos is None: pos = tuple([d/2 for d in dim]) assert len(dim) == len(pos)-1, 'Position has to specify one less coordinates than dimensions!' data = np.zeros(dim) index = list(pos) index.insert(axis, slice(None)) # [:] for the filament axis! pos everywhere else! data[tuple(index)] = 1 return Field(data=data, scale=scale, vector=False)
[docs]def create_shape_voxel(dim, pos=None, scale=1.0): """Creates a Field object with the shape of a single voxel in arbitrary dimension. Parameters ---------- dim : tuple The dimensions of the grid. pos : tuple, optional The position of the voxel. scale: tuple of float Scaling along the dimensions of the underlying data. """ _log.debug('Calling __init__') assert len(dim) > 1, 'Only usable for multidimensional Fields (at least 2 dimensions)!' if pos is None: pos = tuple([d/2 for d in dim]) assert len(dim) == len(pos), 'Parameters dim and pos must have the same dimensions!' data = np.zeros(dim) data[pos] = 1 return Field(data=data, scale=scale, vector=False)