diff --git a/code_formatter.sh b/code_formatter.sh index 2f67db6..90221fa 100755 --- a/code_formatter.sh +++ b/code_formatter.sh @@ -2,7 +2,7 @@ ####################################### -required_command="yapf unexpand" +required_command="yapf" code_directories="pygem tests" ####################################### @@ -38,31 +38,15 @@ for dir in $code_directories; do done [[ $# != 0 ]] && python_files=$@ -# The files unvhandler.py and params.py can not be formatted because there are -# strings with spaces inside that otherwise would be converted in tabs. -python_files_true="" -for file in $python_files; do - if [ $file != "pygem/unvhandler.py" ] && [ $file != "pygem/params.py" ]; then - python_files_true="$python_files_true $file" - fi -done -# Here the important part: -# - first, yapf format the files; it works very well just setting few option -# by command line, but at the moment it has bugs for tabs, so uses spaces. -# - second, convert 4 spaces to tab character -# - third, you can look a very pretty code -for file in $python_files_true; do +# Here the important part: yapf format the files. +for file in $python_files; do echo "Making beatiful $file..." [[ ! -f $file ]] && echo "$file does not exist; $0 -h for more info" && exit yapf --style='{ based_on_style: pep8, indent_width: 4, - dedent_closing_brackets = true, - coalesce_brackets = true, - column_limit = 80 + column_limit: 80 }' -i $file - unexpand -t 4 $file > tmp.py - mv tmp.py $file -done \ No newline at end of file +done diff --git a/pygem/__init__.py b/pygem/__init__.py index 43ae037..74c5446 100644 --- a/pygem/__init__.py +++ b/pygem/__init__.py @@ -1,9 +1,9 @@ """ """ __all__ = [ - 'affine', 'filehandler', 'freeform', 'radial', 'openfhandler', 'params', - 'stlhandler', 'unvhandler', 'vtkhandler', 'nurbshandler', 'stephandler', - 'igeshandler', 'utils', 'gui', 'khandler', 'idw', 'params_idw' + 'affine', 'filehandler', 'freeform', 'radial', 'openfhandler', 'params', + 'stlhandler', 'unvhandler', 'vtkhandler', 'nurbshandler', 'stephandler', + 'igeshandler', 'utils', 'gui', 'khandler', 'idw', 'params_idw' ] from . import affine diff --git a/pygem/affine.py b/pygem/affine.py index f9809b8..58dfcd6 100644 --- a/pygem/affine.py +++ b/pygem/affine.py @@ -9,180 +9,175 @@ def angles2matrix(rot_z=0, rot_y=0, rot_x=0): - """ - This method returns the rotation matrix for given rotations around z, y and - x axes. The output rotation matrix is equal to the composition of the - individual rotations. Rotations are counter-clockwise. The default value of - the three rotations is zero. - - :param float rot_z: rotation angle (in radians) around z-axis. - :param float rot_y: rotation angle (in radians) around y-axis. - :param float rot_x: rotation angle (in radians) around x-axis. - - :return: rot_matrix: rotation matrix for the given angles. The matrix shape - is always (3, 3). - :rtype: numpy.ndarray - - :Example: - - >>> import pygem.affine as at - >>> import numpy as np - >>> from math import radians - >>> # Example of a rotation around x, y, z axis - >>> rotz = radians(10) - >>> roty = radians(20) - >>> rotx = radians(30) - >>> rot_matrix = at.angles2matrix(rotz, roty, rotx) - - .. note:: - - - The direction of rotation is given by the right-hand rule. - - When applying the rotation to a vector, the vector should be column - vector to the right of the rotation matrix. - """ - rot_matrix = [] - if rot_z: - cos = math.cos(rot_z) - sin = math.sin(rot_z) - rot_matrix.append( - np.array([cos, -sin, 0, sin, cos, 0, 0, 0, 1]).reshape((3, 3)) - ) - if rot_y: - cos = math.cos(rot_y) - sin = math.sin(rot_y) - rot_matrix.append( - np.array([cos, 0, sin, 0, 1, 0, -sin, 0, cos]).reshape((3, 3)) - ) - if rot_x: - cos = math.cos(rot_x) - sin = math.sin(rot_x) - rot_matrix.append( - np.array([1, 0, 0, 0, cos, -sin, 0, sin, cos]).reshape((3, 3)) - ) - if rot_matrix: - return reduce(np.dot, rot_matrix[::-1]) - return np.eye(3) + """ + This method returns the rotation matrix for given rotations around z, y and + x axes. The output rotation matrix is equal to the composition of the + individual rotations. Rotations are counter-clockwise. The default value of + the three rotations is zero. + + :param float rot_z: rotation angle (in radians) around z-axis. + :param float rot_y: rotation angle (in radians) around y-axis. + :param float rot_x: rotation angle (in radians) around x-axis. + + :return: rot_matrix: rotation matrix for the given angles. The matrix shape + is always (3, 3). + :rtype: numpy.ndarray + + :Example: + + >>> import pygem.affine as at + >>> import numpy as np + >>> from math import radians + >>> # Example of a rotation around x, y, z axis + >>> rotz = radians(10) + >>> roty = radians(20) + >>> rotx = radians(30) + >>> rot_matrix = at.angles2matrix(rotz, roty, rotx) + + .. note:: + + - The direction of rotation is given by the right-hand rule. + - When applying the rotation to a vector, the vector should be column + vector to the right of the rotation matrix. + """ + rot_matrix = [] + if rot_z: + cos = math.cos(rot_z) + sin = math.sin(rot_z) + rot_matrix.append( + np.array([cos, -sin, 0, sin, cos, 0, 0, 0, 1]).reshape((3, 3))) + if rot_y: + cos = math.cos(rot_y) + sin = math.sin(rot_y) + rot_matrix.append( + np.array([cos, 0, sin, 0, 1, 0, -sin, 0, cos]).reshape((3, 3))) + if rot_x: + cos = math.cos(rot_x) + sin = math.sin(rot_x) + rot_matrix.append( + np.array([1, 0, 0, 0, cos, -sin, 0, sin, cos]).reshape((3, 3))) + if rot_matrix: + return reduce(np.dot, rot_matrix[::-1]) + return np.eye(3) def to_reduced_row_echelon_form(matrix): - """ - This method computes the reduced row echelon form (a.k.a. row canonical - form) of a matrix. The code is taken from - https://rosettacode.org/wiki/Reduced_row_echelon_form#Python and edited with - minor changes. - - :param matrix matrix: matrix to be reduced. - - :return matrix: the reduced matrix. - :rtype: matrix - - :Example: - - >>> import pygem.affine as at - >>> matrix = [[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]] - >>> rref_matrix = at.to_reduced_row_echelon_form(matrix) - - .. note:: - - `matrix` will change after calling this function. - """ - lead = 0 - row_count = len(matrix) - column_count = len(matrix[0]) - for r in range(row_count): - if lead >= column_count: - return matrix - i = r - while matrix[i][lead] == 0: - i += 1 - if i == row_count: - i = r - lead += 1 - if column_count == lead: - return matrix - matrix[i], matrix[r] = matrix[r], matrix[i] - lv = matrix[r][lead] - matrix[r] = [mrx / float(lv) for mrx in matrix[r]] - for i in range(row_count): - if i != r: - lv = matrix[i][lead] - matrix[i] = [ - iv - lv * rv for rv, iv in zip(matrix[r], matrix[i]) - ] - lead += 1 - return matrix + """ + This method computes the reduced row echelon form (a.k.a. row canonical + form) of a matrix. The code is taken from + https://rosettacode.org/wiki/Reduced_row_echelon_form#Python and edited with + minor changes. + + :param matrix matrix: matrix to be reduced. + + :return matrix: the reduced matrix. + :rtype: matrix + + :Example: + + >>> import pygem.affine as at + >>> matrix = [[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]] + >>> rref_matrix = at.to_reduced_row_echelon_form(matrix) + + .. note:: + + `matrix` will change after calling this function. + """ + lead = 0 + row_count = len(matrix) + column_count = len(matrix[0]) + for r in range(row_count): + if lead >= column_count: + return matrix + i = r + while matrix[i][lead] == 0: + i += 1 + if i == row_count: + i = r + lead += 1 + if column_count == lead: + return matrix + matrix[i], matrix[r] = matrix[r], matrix[i] + lv = matrix[r][lead] + matrix[r] = [mrx / float(lv) for mrx in matrix[r]] + for i in range(row_count): + if i != r: + lv = matrix[i][lead] + matrix[i] = [ + iv - lv * rv for rv, iv in zip(matrix[r], matrix[i]) + ] + lead += 1 + return matrix def affine_points_fit(points_start, points_end): - """ - Fit an affine transformation from starting points to ending points through a - least square procedure. - - :param numpy.ndarray points_start: set of starting points. - :param numpy.ndarray points_end: set of ending points. - - :return: transform_vector: function that transforms a vector according to - the affine map. It takes a source vector and return a vector transformed - by the reduced row echelon form of the map. - :rtype: function - - :Example: - - >>> import pygem.affine as at - - >>> # Example of a rotation (affine transformation) - >>> p_start = np.array([[1,0,0], [0,1,0], [0,0,1], [0,0,0]]) - >>> p_end = np.array([[0,1,0], [-1,0,0], [0,0,1], [0,0,0]]) - >>> v_test = np.array([1., 2., 3.]) - >>> transformation = at.affine_points_fit(p_start, p_end) - >>> v_trans = transformation(v_test) - """ - if len(points_start) != len(points_end): - raise RuntimeError("points_start and points_end must be of same size.") - - dim = len(points_start[0]) - if len(points_start) < dim: - raise RuntimeError( - "Too few starting points => under-determined system." - ) - - # Fill an an empty (dim+1) x (dim) matrix - c = [[0.0 for a in range(dim)] for i in range(dim + 1)] - for j in range(dim): - for k in range(dim + 1): - for i, pnts_i in enumerate(points_start): - qt = list(pnts_i) + [1] - c[k][j] += qt[k] * points_end[i][j] - - # Fill an an empty (dim+1) x (dim+1) matrix - Q = [[0.0 for a in range(dim)] + [0] for i in range(dim + 1)] - for qi in points_start: - qt = list(qi) + [1] - for i in range(dim + 1): - for j in range(dim + 1): - Q[i][j] += qt[i] * qt[j] - - # Augement Q with c and get the reduced row echelon form of the result - affine_matrix = [Q[i] + c[i] for i in range(dim + 1)] - - if np.linalg.cond(affine_matrix) < 1 / sys.float_info.epsilon: - rref_aff_matrix = to_reduced_row_echelon_form(affine_matrix) - rref_aff_matrix = np.array(rref_aff_matrix)[:, 4:] - else: - raise RuntimeError( - "Error: singular matrix. Points are probably coplanar." - ) - - def transform_vector(source): - """ - Transform a vector according to the affine map. - - :param numpy.ndarray source: vector to be transformed. - - :return destination: numpy.ndarray representing the transformed vector. - :rtype: numpy.ndarray - """ - destination = source.dot(rref_aff_matrix[:-1]) + rref_aff_matrix[-1] - return destination - - return transform_vector + """ + Fit an affine transformation from starting points to ending points through a + least square procedure. + + :param numpy.ndarray points_start: set of starting points. + :param numpy.ndarray points_end: set of ending points. + + :return: transform_vector: function that transforms a vector according to + the affine map. It takes a source vector and return a vector transformed + by the reduced row echelon form of the map. + :rtype: function + + :Example: + + >>> import pygem.affine as at + + >>> # Example of a rotation (affine transformation) + >>> p_start = np.array([[1,0,0], [0,1,0], [0,0,1], [0,0,0]]) + >>> p_end = np.array([[0,1,0], [-1,0,0], [0,0,1], [0,0,0]]) + >>> v_test = np.array([1., 2., 3.]) + >>> transformation = at.affine_points_fit(p_start, p_end) + >>> v_trans = transformation(v_test) + """ + if len(points_start) != len(points_end): + raise RuntimeError("points_start and points_end must be of same size.") + + dim = len(points_start[0]) + if len(points_start) < dim: + raise RuntimeError( + "Too few starting points => under-determined system.") + + # Fill an an empty (dim+1) x (dim) matrix + c = [[0.0 for a in range(dim)] for i in range(dim + 1)] + for j in range(dim): + for k in range(dim + 1): + for i, pnts_i in enumerate(points_start): + qt = list(pnts_i) + [1] + c[k][j] += qt[k] * points_end[i][j] + + # Fill an an empty (dim+1) x (dim+1) matrix + Q = [[0.0 for a in range(dim)] + [0] for i in range(dim + 1)] + for qi in points_start: + qt = list(qi) + [1] + for i in range(dim + 1): + for j in range(dim + 1): + Q[i][j] += qt[i] * qt[j] + + # Augement Q with c and get the reduced row echelon form of the result + affine_matrix = [Q[i] + c[i] for i in range(dim + 1)] + + if np.linalg.cond(affine_matrix) < 1 / sys.float_info.epsilon: + rref_aff_matrix = to_reduced_row_echelon_form(affine_matrix) + rref_aff_matrix = np.array(rref_aff_matrix)[:, 4:] + else: + raise RuntimeError( + "Error: singular matrix. Points are probably coplanar.") + + def transform_vector(source): + """ + Transform a vector according to the affine map. + + :param numpy.ndarray source: vector to be transformed. + + :return destination: numpy.ndarray representing the transformed vector. + :rtype: numpy.ndarray + """ + destination = source.dot(rref_aff_matrix[:-1]) + rref_aff_matrix[-1] + return destination + + return transform_vector diff --git a/pygem/filehandler.py b/pygem/filehandler.py index 457e02e..d0bdf65 100644 --- a/pygem/filehandler.py +++ b/pygem/filehandler.py @@ -5,74 +5,72 @@ class FileHandler(object): - """ - A base class for file handling. + """ + A base class for file handling. - :cvar string infile: name of the input file to be processed. - :cvar string outfile: name of the output file where to write in. - :cvar list extensions: extensions of the input/output files. It is specific for each - subclass. - """ + :cvar string infile: name of the input file to be processed. + :cvar string outfile: name of the output file where to write in. + :cvar list extensions: extensions of the input/output files. It is specific for each + subclass. + """ - def __init__(self): - self.infile = None - self.outfile = None - self.extensions = [] + def __init__(self): + self.infile = None + self.outfile = None + self.extensions = [] - def parse(self, *args): - """ - Abstract method to parse a specific file. + def parse(self, *args): + """ + Abstract method to parse a specific file. - Not implemented, it has to be implemented in subclasses. - """ - raise NotImplementedError( - "Subclass must implement abstract method " \ - + self.__class__.__name__ + ".parse") + Not implemented, it has to be implemented in subclasses. + """ + raise NotImplementedError( + "Subclass must implement abstract method " \ + + self.__class__.__name__ + ".parse") - def write(self, *args): - """ - Abstract method to write a specific file. + def write(self, *args): + """ + Abstract method to write a specific file. - Not implemented, it has to be implemented in subclasses. - """ - raise NotImplementedError( - "Subclass must implement abstract method " \ - + self.__class__.__name__ + ".write") + Not implemented, it has to be implemented in subclasses. + """ + raise NotImplementedError( + "Subclass must implement abstract method " \ + + self.__class__.__name__ + ".write") - def _check_extension(self, filename): - """ - This private class method checks if the given `filename` has the proper `extension` set - in the child class. If not it raises a ValueError. + def _check_extension(self, filename): + """ + This private class method checks if the given `filename` has the proper `extension` set + in the child class. If not it raises a ValueError. - :param string filename: file to check. - """ - __, file_ext = os.path.splitext(filename) - if file_ext not in self.extensions: - raise ValueError( - 'The input file does not have the proper extension. \ - It is {0!s}, instead of {1!s}.'.format(file_ext, self.extensions) - ) + :param string filename: file to check. + """ + __, file_ext = os.path.splitext(filename) + if file_ext not in self.extensions: + raise ValueError( + 'The input file does not have the proper extension. \ + It is {0!s}, instead of {1!s}.'.format(file_ext, + self.extensions)) - @staticmethod - def _check_filename_type(filename): - """ - This private static method checks if `filename` is a string. If not it raises a TypeError. + @staticmethod + def _check_filename_type(filename): + """ + This private static method checks if `filename` is a string. If not it raises a TypeError. - :param string filename: file to check. - """ - if not isinstance(filename, str): - raise TypeError( - 'The given filename ({0!s}) must be a string'.format(filename) - ) + :param string filename: file to check. + """ + if not isinstance(filename, str): + raise TypeError( + 'The given filename ({0!s}) must be a string'.format(filename)) - def _check_infile_instantiation(self): - """ - This private method checks if `self.infile` is instantiated. If not it means - that nobody called the parse method and `self.infile` is None. If the check fails - it raises a RuntimeError. + def _check_infile_instantiation(self): + """ + This private method checks if `self.infile` is instantiated. If not it means + that nobody called the parse method and `self.infile` is None. If the check fails + it raises a RuntimeError. - """ - if not self.infile: - raise RuntimeError( - "You can not write a file without having parsed one." - ) + """ + if not self.infile: + raise RuntimeError( + "You can not write a file without having parsed one.") diff --git a/pygem/freeform.py b/pygem/freeform.py index 141a482..27adc49 100644 --- a/pygem/freeform.py +++ b/pygem/freeform.py @@ -3,184 +3,183 @@ :Theoretical Insight: - Free Form Deformation is a technique for the efficient, smooth and accurate - geometrical parametrization. It has been proposed the first time in - *Sederberg, Thomas W., and Scott R. Parry. "Free-form deformation of solid - geometric models." ACM SIGGRAPH computer graphics 20.4 (1986): 151-160*. It - consists in three different step: - - - Mapping the physical domain to the reference one with map - :math:`\\boldsymbol{\psi}`. In the code it is named *transformation*. - - - Moving some control points to deform the lattice with :math:`\\hat{T}`. - The movement of the control points is basically the weight (or displacement) - :math:`\\boldsymbol{\mu}` we set in the *parameters file*. - - - Mapping back to the physical domain with map - :math:`\\boldsymbol{\psi}^-1`. In the code it is named - *inverse_transformation*. - - FFD map (:math:`T`) is the composition of the three maps, that is - - .. math:: T(\\cdot, \\boldsymbol{\\mu}) = (\\Psi^{-1} \\circ \\hat{T} \\circ - \\Psi) (\\cdot, \\boldsymbol{\\mu}) - - In this way, every point inside the FFD box changes it position according to - - .. math:: \\boldsymbol{P} = \\boldsymbol{\psi}^-1 \\left( \\sum_{l=0} ^L - \\sum_{m=0} ^M \\sum_{n=0} ^N - \\mathsf{b}_{lmn}(\\boldsymbol{\\psi}(\\boldsymbol{P}_0)) - \\boldsymbol{\\mu}_{lmn} \\right) - - where :math:`\\mathsf{b}_{lmn}` are Bernstein polynomials. We improve the - traditional version by allowing a rotation of the FFD lattice in order to - give more flexibility to the tool. - - You can try to add more shapes to the lattice to allow more and more - involved transformations. + Free Form Deformation is a technique for the efficient, smooth and accurate + geometrical parametrization. It has been proposed the first time in + *Sederberg, Thomas W., and Scott R. Parry. "Free-form deformation of solid + geometric models." ACM SIGGRAPH computer graphics 20.4 (1986): 151-160*. It + consists in three different step: + + - Mapping the physical domain to the reference one with map + :math:`\\boldsymbol{\psi}`. In the code it is named *transformation*. + + - Moving some control points to deform the lattice with :math:`\\hat{T}`. + The movement of the control points is basically the weight (or displacement) + :math:`\\boldsymbol{\mu}` we set in the *parameters file*. + + - Mapping back to the physical domain with map + :math:`\\boldsymbol{\psi}^-1`. In the code it is named + *inverse_transformation*. + + FFD map (:math:`T`) is the composition of the three maps, that is + + .. math:: T(\\cdot, \\boldsymbol{\\mu}) = (\\Psi^{-1} \\circ \\hat{T} \\circ + \\Psi) (\\cdot, \\boldsymbol{\\mu}) + + In this way, every point inside the FFD box changes it position according to + + .. math:: \\boldsymbol{P} = \\boldsymbol{\psi}^-1 \\left( \\sum_{l=0} ^L + \\sum_{m=0} ^M \\sum_{n=0} ^N + \\mathsf{b}_{lmn}(\\boldsymbol{\\psi}(\\boldsymbol{P}_0)) + \\boldsymbol{\\mu}_{lmn} \\right) + + where :math:`\\mathsf{b}_{lmn}` are Bernstein polynomials. We improve the + traditional version by allowing a rotation of the FFD lattice in order to + give more flexibility to the tool. + + You can try to add more shapes to the lattice to allow more and more + involved transformations. """ import numpy as np from scipy import special import pygem.affine as at + class FFD(object): - """ - Class that handles the Free Form Deformation on the mesh points. - - :param FFDParameters ffd_parameters: parameters of the Free Form - Deformation. - :param numpy.ndarray original_mesh_points: coordinates of the original - points of the mesh. - - :cvar FFDParameters parameters: parameters of the Free Form Deformation. - :cvar numpy.ndarray original_mesh_points: coordinates of the original points - of the mesh. The shape is `n_points`-by-3. - :cvar numpy.ndarray modified_mesh_points: coordinates of the points of the - deformed mesh. The shape is `n_points`-by-3. - - :Example: - - >>> import pygem.freeform as ffd - >>> import pygem.params as ffdp - >>> import numpy as np - >>> ffd_parameters = ffdp.FFDParameters() - >>> ffd_parameters.read_parameters('tests/test_datasets/parameters_test_ffd_sphere.prm') - >>> original_mesh_points = np.load('tests/test_datasets/meshpoints_sphere_orig.npy') - >>> free_form = ffd.FFD(ffd_parameters, original_mesh_points) - >>> free_form.perform() - >>> new_mesh_points = free_form.modified_mesh_points - """ - - def __init__(self, ffd_parameters, original_mesh_points): - self.parameters = ffd_parameters - self.original_mesh_points = original_mesh_points - self.modified_mesh_points = None - - def perform(self): - """ - This method performs the deformation on the mesh points. After the - execution it sets `self.modified_mesh_points`. - """ - # translation and then affine transformation - translation = self.parameters.origin_box - - physical_frame = np.array([ - self.parameters.position_vertex_1 - translation, - self.parameters.position_vertex_2 - translation, - self.parameters.position_vertex_3 - translation, [0, 0, 0] - ]) - reference_frame = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 0]]) - - transformation = at.affine_points_fit(physical_frame, reference_frame) - inverse_transformation = at.affine_points_fit( - reference_frame, physical_frame - ) - - # apply transformation to original mesh points - reference_frame_mesh_points = self._transform_points( - self.original_mesh_points - translation, transformation - ) - - # select mesh points inside bounding box - mesh_points = reference_frame_mesh_points[( - reference_frame_mesh_points[:, 0] >= 0. - ) & (reference_frame_mesh_points[:, 0] <= 1.) & ( - reference_frame_mesh_points[:, 1] >= 0. - ) & (reference_frame_mesh_points[:, 1] <= 1.) & ( - reference_frame_mesh_points[:, 2] >= 0. - ) & (reference_frame_mesh_points[:, 2] <= 1.)] - (n_rows_mesh, n_cols_mesh) = mesh_points.shape - - # Initialization. In order to exploit the contiguity in memory the - # following are transposed - (dim_n_mu, dim_m_mu, dim_t_mu) = self.parameters.array_mu_x.shape - bernstein_x = np.zeros((dim_n_mu, n_rows_mesh)) - bernstein_y = np.zeros((dim_m_mu, n_rows_mesh)) - bernstein_z = np.zeros((dim_t_mu, n_rows_mesh)) - shift_mesh_points = np.zeros((n_cols_mesh, n_rows_mesh)) - - for i in range(0, dim_n_mu): - aux1 = np.power((1 - mesh_points[:, 0]), dim_n_mu - 1 - i) - aux2 = np.power(mesh_points[:, 0], i) - bernstein_x[i, :] = special.binom(dim_n_mu - 1, - i) * np.multiply(aux1, aux2) - - for i in range(0, dim_m_mu): - aux1 = np.power((1 - mesh_points[:, 1]), dim_m_mu - 1 - i) - aux2 = np.power(mesh_points[:, 1], i) - bernstein_y[i, :] = special.binom(dim_m_mu - 1, - i) * np.multiply(aux1, aux2) - - for i in range(0, dim_t_mu): - aux1 = np.power((1 - mesh_points[:, 2]), dim_t_mu - 1 - i) - aux2 = np.power(mesh_points[:, 2], i) - bernstein_z[i, :] = special.binom(dim_t_mu - 1, - i) * np.multiply(aux1, aux2) - - aux_x = 0. - aux_y = 0. - aux_z = 0. - for j in range(0, dim_m_mu): - for k in range(0, dim_t_mu): - bernstein_yz = np.multiply(bernstein_y[j, :], bernstein_z[k, :]) - for i in range(0, dim_n_mu): - aux = np.multiply(bernstein_x[i, :], bernstein_yz) - aux_x += aux * self.parameters.array_mu_x[i, j, k] - aux_y += aux * self.parameters.array_mu_y[i, j, k] - aux_z += aux * self.parameters.array_mu_z[i, j, k] - shift_mesh_points[0, :] += aux_x - shift_mesh_points[1, :] += aux_y - shift_mesh_points[2, :] += aux_z - - # shift_mesh_points needs to be transposed to be summed with mesh_points - # apply inverse transformation to shifted mesh points - new_mesh_points = self._transform_points(np.transpose(shift_mesh_points) + - mesh_points, inverse_transformation) + \ - translation - - # merge non-shifted mesh points with shifted ones - self.modified_mesh_points = np.copy(self.original_mesh_points) - self.modified_mesh_points[(reference_frame_mesh_points[:,0] >= 0.) & - (reference_frame_mesh_points[:,0] <= 1.) & - (reference_frame_mesh_points[:,1] >= 0.) & - (reference_frame_mesh_points[:,1] <= 1.) & - (reference_frame_mesh_points[:,2] >= 0.) & - (reference_frame_mesh_points[:,2] <= 1.)] \ - = new_mesh_points - - @staticmethod - def _transform_points(original_points, transformation): - """ - This private static method transforms the points according to the affine - transformation taken from affine_points_fit method. - - :param numpy.ndarray original_points: coordinates of the original - points. - :param function transformation: affine transformation taken from - affine_points_fit method. - - :return: modified_points: coordinates of the modified points. - :rtype: numpy.ndarray - """ - return transformation(original_points) + """ + Class that handles the Free Form Deformation on the mesh points. + + :param FFDParameters ffd_parameters: parameters of the Free Form + Deformation. + :param numpy.ndarray original_mesh_points: coordinates of the original + points of the mesh. + + :cvar FFDParameters parameters: parameters of the Free Form Deformation. + :cvar numpy.ndarray original_mesh_points: coordinates of the original points + of the mesh. The shape is `n_points`-by-3. + :cvar numpy.ndarray modified_mesh_points: coordinates of the points of the + deformed mesh. The shape is `n_points`-by-3. + + :Example: + + >>> import pygem.freeform as ffd + >>> import pygem.params as ffdp + >>> import numpy as np + >>> ffd_parameters = ffdp.FFDParameters() + >>> ffd_parameters.read_parameters('tests/test_datasets/parameters_test_ffd_sphere.prm') + >>> original_mesh_points = np.load('tests/test_datasets/meshpoints_sphere_orig.npy') + >>> free_form = ffd.FFD(ffd_parameters, original_mesh_points) + >>> free_form.perform() + >>> new_mesh_points = free_form.modified_mesh_points + """ + + def __init__(self, ffd_parameters, original_mesh_points): + self.parameters = ffd_parameters + self.original_mesh_points = original_mesh_points + self.modified_mesh_points = None + + def perform(self): + """ + This method performs the deformation on the mesh points. After the + execution it sets `self.modified_mesh_points`. + """ + # translation and then affine transformation + translation = self.parameters.origin_box + + physical_frame = np.array([ + self.parameters.position_vertex_1 - translation, + self.parameters.position_vertex_2 - translation, + self.parameters.position_vertex_3 - translation, [0, 0, 0] + ]) + reference_frame = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 0]]) + + transformation = at.affine_points_fit(physical_frame, reference_frame) + inverse_transformation = at.affine_points_fit(reference_frame, + physical_frame) + + # apply transformation to original mesh points + reference_frame_mesh_points = self._transform_points( + self.original_mesh_points - translation, transformation) + + # select mesh points inside bounding box + mesh_points = reference_frame_mesh_points[ + (reference_frame_mesh_points[:, 0] >= 0.) + & (reference_frame_mesh_points[:, 0] <= 1.) & + (reference_frame_mesh_points[:, 1] >= 0.) & + (reference_frame_mesh_points[:, 1] <= 1.) & + (reference_frame_mesh_points[:, 2] >= 0.) & + (reference_frame_mesh_points[:, 2] <= 1.)] + (n_rows_mesh, n_cols_mesh) = mesh_points.shape + + # Initialization. In order to exploit the contiguity in memory the + # following are transposed + (dim_n_mu, dim_m_mu, dim_t_mu) = self.parameters.array_mu_x.shape + bernstein_x = np.zeros((dim_n_mu, n_rows_mesh)) + bernstein_y = np.zeros((dim_m_mu, n_rows_mesh)) + bernstein_z = np.zeros((dim_t_mu, n_rows_mesh)) + shift_mesh_points = np.zeros((n_cols_mesh, n_rows_mesh)) + + for i in range(0, dim_n_mu): + aux1 = np.power((1 - mesh_points[:, 0]), dim_n_mu - 1 - i) + aux2 = np.power(mesh_points[:, 0], i) + bernstein_x[i, :] = special.binom(dim_n_mu - 1, i) * np.multiply( + aux1, aux2) + + for i in range(0, dim_m_mu): + aux1 = np.power((1 - mesh_points[:, 1]), dim_m_mu - 1 - i) + aux2 = np.power(mesh_points[:, 1], i) + bernstein_y[i, :] = special.binom(dim_m_mu - 1, i) * np.multiply( + aux1, aux2) + + for i in range(0, dim_t_mu): + aux1 = np.power((1 - mesh_points[:, 2]), dim_t_mu - 1 - i) + aux2 = np.power(mesh_points[:, 2], i) + bernstein_z[i, :] = special.binom(dim_t_mu - 1, i) * np.multiply( + aux1, aux2) + + aux_x = 0. + aux_y = 0. + aux_z = 0. + for j in range(0, dim_m_mu): + for k in range(0, dim_t_mu): + bernstein_yz = np.multiply(bernstein_y[j, :], bernstein_z[k, :]) + for i in range(0, dim_n_mu): + aux = np.multiply(bernstein_x[i, :], bernstein_yz) + aux_x += aux * self.parameters.array_mu_x[i, j, k] + aux_y += aux * self.parameters.array_mu_y[i, j, k] + aux_z += aux * self.parameters.array_mu_z[i, j, k] + shift_mesh_points[0, :] += aux_x + shift_mesh_points[1, :] += aux_y + shift_mesh_points[2, :] += aux_z + + # shift_mesh_points needs to be transposed to be summed with mesh_points + # apply inverse transformation to shifted mesh points + new_mesh_points = self._transform_points(np.transpose(shift_mesh_points) + + mesh_points, inverse_transformation) + \ + translation + + # merge non-shifted mesh points with shifted ones + self.modified_mesh_points = np.copy(self.original_mesh_points) + self.modified_mesh_points[(reference_frame_mesh_points[:,0] >= 0.) & + (reference_frame_mesh_points[:,0] <= 1.) & + (reference_frame_mesh_points[:,1] >= 0.) & + (reference_frame_mesh_points[:,1] <= 1.) & + (reference_frame_mesh_points[:,2] >= 0.) & + (reference_frame_mesh_points[:,2] <= 1.)] \ + = new_mesh_points + + @staticmethod + def _transform_points(original_points, transformation): + """ + This private static method transforms the points according to the affine + transformation taken from affine_points_fit method. + + :param numpy.ndarray original_points: coordinates of the original + points. + :param function transformation: affine transformation taken from + affine_points_fit method. + + :return: modified_points: coordinates of the modified points. + :rtype: numpy.ndarray + """ + return transformation(original_points) diff --git a/pygem/gui.py b/pygem/gui.py index fdeec79..d1c8e42 100644 --- a/pygem/gui.py +++ b/pygem/gui.py @@ -2,14 +2,14 @@ Utilities for handling the Graphic Unit Interface. .. todo:: - Switch to Ttk instead of Tk for a better look of the GUI + Switch to Ttk instead of Tk for a better look of the GUI """ try: - import tkinter - from tkinter.filedialog import askopenfilename + import tkinter + from tkinter.filedialog import askopenfilename except: - import Tkinter as tkinter - from tkFileDialog import askopenfilename + import Tkinter as tkinter + from tkFileDialog import askopenfilename import os import webbrowser @@ -17,247 +17,231 @@ class Gui(object): - """ - The class for the Graphic Unit Interface. - - :cvar string filename_geometry: input geometry to be morphed. - :cvar string filename_parameters: input parameters file for FFD. - :cvar int check_var_dump_orig: dump or not the original FFD lattice. - :cvar int check_var_dump_morphed: dump or not the morphed FFD lattice. - :cvar string outfilename: name of the output file geometry. - The extension of the file is set automatically equal to the on of input file 'filename_geometry'. - :cvar string outfilename_lattice_orig: name of the dumped file for the original lattice. - The extension of the file is set automatically equal to '.vtk'. - :cvar string outfilename_lattice_mod: name of the dumped file for the morphed lattice. - The extension of the file is set automatically equal to '.vtk'. - :cvar tkinter.Tk root: main window object of the GUI. - :cvar string print_geometry_path: geometry path to be printed close to the 'pick geometry' button. - :cvar string print_parameter_path: parameters file path to be printed close to the - 'pick parameters' button. - :cvar tkinter.Label label_geo: label related to 'print_geometry_path'. - :cvar tkinter.Label label_params: label related to 'print_parameters_path'. - :cvar string url: url of the github page of PyGeM. - :cvar tkinter.Canvas logo_panel: canvas for PyGeM logo. - :cvar tkinter.PhotoImage img: PyGeM logo. - :cvar tkinter.Frame orig_geo_frame: frame for plotting of the original geometry. - :cvar tkinter.Frame mod_geo_frame: frame for plotting of the final geometry. - """ - - def __init__(self): - self.root = tkinter.Tk() - self.root.resizable(width=False, height=False) - self.root.minsize(width=1400, height=400) - self.root.maxsize(width=1400, height=400) - self.root.title('PyGeM') - self.filename_geometry = tkinter.StringVar() - self.filename_parameters = tkinter.StringVar() - self.check_var_dump_orig = tkinter.IntVar() - self.check_var_dump_morphed = tkinter.IntVar() - self.outfilename = tkinter.StringVar() - self.outfilename_lattice_orig = tkinter.StringVar() - self.outfilename_lattice_mod = tkinter.StringVar() - self.print_geometry_path = tkinter.StringVar() - self.print_parameter_path = tkinter.StringVar() - self.label_geo = None - self.label_params = None - self.url = 'https://github.com/mathLab/PyGeM' - self.logo_panel = None - self.img = None - self.orig_geo_frame = None - self.mod_geo_frame = None - - def _chose_geometry(self): - """ - The private method explores the file system and allows to select the wanted geometry. - Up to now, you can select only IGES, OpenFOAM, STL, UNV or VTK geometry file. - """ - self.filename_geometry = askopenfilename(filetypes=[("IGES File", ('*.iges', '*.igs')), \ - ("OpenFOAM File", '*'), ('STL File', '*.stl'), ('UNV File', '*.unv'), \ - ('VTK File', '*.vtk'), ('STEP File', ('*.step, *.stp')) ('All', '*')]) - self.print_geometry_path.set(self.filename_geometry) - self.label_geo.configure(fg='green') - - def _chose_parameters(self): - """ - The private method explores the file system and allows to select the wanted parameters file. - It visualizes only .prm files. - """ - self.filename_parameters = askopenfilename( - filetypes=[("Params File", "*.prm")] - ) - self.print_parameter_path.set(self.filename_parameters) - self.label_params.configure(fg='green') - - def _run_simulation(self): - """ - The private method runs the geometrical morphing. - """ - import pygem as pg - params = pg.params.FFDParameters() - params.read_parameters(filename=self.filename_parameters) - - file_extension_in = os.path.splitext(self.filename_geometry)[-1] - ext_handlers = { - '.stl': pg.stlhandler.StlHandler(), - '.iges': pg.igeshandler.IgesHandler(), - '.igs': pg.igeshandler.IgesHandler(), - '.unv': pg.unvhandler.UnvHandler(), - '': pg.openfhandler.OpenFoamHandler(), - '.vtk': pg.vtkhandler.VtkHandler(), - '.stp': pg.stephandler.StepHandler(), - '.step': pg.stephandler.StepHandler(), - } - - if file_extension_in in ext_handlers: - geo_handler = ext_handlers[file_extension_in] - else: - raise NotImplementedError("Format not implemented yet.") - - mesh_points = geo_handler.parse(self.filename_geometry) - free_form = pg.freeform.FFD(params, mesh_points) - free_form.perform() - new_mesh_points = free_form.modified_mesh_points - - geo_handler.write( - new_mesh_points, self.outfilename.get() + file_extension_in - ) - - if self.check_var_dump_orig.get() == 1: - pg.utils.write_bounding_box( - params, self.outfilename_lattice_orig.get() + '.vtk', False - ) - if self.check_var_dump_morphed.get() == 1: - pg.utils.write_bounding_box( - params, self.outfilename_lattice_mod.get() + '.vtk', True - ) - - if file_extension_in in ['.vtk', '.stl', '.iges', '.igs']: - figure_in = geo_handler.plot() - figure_in.set_size_inches(4, 3) - FigureCanvasTkAgg(figure_in, master=self.orig_geo_frame).get_tk_widget().grid(row=1, column=0, \ - padx=5, pady=5) - figure_out = geo_handler.plot( - self.outfilename.get() + file_extension_in - ) - figure_out.set_size_inches(4, 3) - FigureCanvasTkAgg(figure_out, master=self.mod_geo_frame).get_tk_widget().grid(row=1, column=0, \ - padx=5, pady=5) - - def _goto_website(self): - """ - The private method opens the PyGeM main page on github. - It is used for info about PyGeM in the menu. - """ - webbrowser.open(self.url) - - def _main(self): - """ - The private method inizializes and visualizes the window. - """ - self.logo_panel = tkinter.Canvas(self.root, height=60, width=60) - self.logo_panel.pack(side="bottom", padx=5, pady=5, anchor=tkinter.SE) - try: - self.img = tkinter.PhotoImage( - master=self.logo_panel, file='readme/logo_PyGeM_gui.gif' - ) - except: - self.img = tkinter.PhotoImage( - master=self.logo_panel, file='../readme/logo_PyGeM_gui.gif' - ) - self.logo_panel.create_image(35, 35, image=self.img) - - self.orig_geo_frame = tkinter.Frame( - self.root, height=450, width=360, bg='#c1d0f0' - ) - self.orig_geo_frame.pack(side="left", padx=5, pady=5) - self.orig_geo_frame.pack_propagate(0) - tkinter.Label(self.orig_geo_frame, text="INPUT GEOMETRY", bg='#c1d0f0', \ - font=("Arial", 20)).grid(row=0, column=0, padx=3, pady=3) - - self.mod_geo_frame = tkinter.Frame( - self.root, height=450, width=360, bg='#80ff80', padx=5, pady=5 - ) - self.mod_geo_frame.pack(side="right", padx=5, pady=5) - self.mod_geo_frame.pack_propagate(0) - tkinter.Label(self.mod_geo_frame, text="OUTPUT GEOMETRY", bg='#80ff80', \ - font=("Arial", 20)).grid(row=0, column=0, padx=3, pady=3) - - code_frame = tkinter.Frame( - self.root, - height=490, - width=360, - relief=tkinter.GROOVE, - borderwidth=1 - ) - code_frame.pack(padx=5, pady=5) - code_frame.pack_propagate(0) - - # Buttons 1 - tkinter.Button(code_frame, text="Pick the geometry", \ - command=self._chose_geometry).grid(row=0, column=0, padx=3, pady=3) - self.label_geo = tkinter.Label( - code_frame, textvariable=self.print_geometry_path, fg='red' - ) - self.print_geometry_path.set("No geometry chosen!") - self.label_geo.grid(row=0, column=1, padx=3, pady=3) - - # Button 2 - tkinter.Button(code_frame, text="Pick the parameters", \ - command=self._chose_parameters).grid(row=1, column=0, padx=3, pady=3) - self.label_params = tkinter.Label( - code_frame, textvariable=self.print_parameter_path, fg='red' - ) - self.print_parameter_path.set("No parameters file chosen!") - self.label_params.grid(row=1, column=1, padx=3, pady=3) - - # Entry - tkinter.Label( - code_frame, text="Output geometry file" - ).grid( - row=2, column=0, padx=3, pady=3 - ) - tkinter.Entry(code_frame, bd=5, textvariable=self.outfilename).grid(row=2, column=1, \ - padx=3, pady=3) - - # Checkboxes - tkinter.Checkbutton(code_frame, text="Dump Original FFD lattice", \ - variable=self.check_var_dump_orig, onvalue=1, offvalue=0, height=3, width=20).grid(row=3, \ - column=0) - tkinter.Entry( - code_frame, bd=5, textvariable=self.outfilename_lattice_orig - ).grid( - row=3, column=1 - ) - - tkinter.Checkbutton(code_frame, text="Dump Morphed FFD lattice", \ - variable=self.check_var_dump_morphed, onvalue=1, offvalue=0, height=3, width=20).grid(row=4, \ - column=0) - tkinter.Entry( - code_frame, bd=5, textvariable=self.outfilename_lattice_mod - ).grid( - row=4, column=1 - ) - - # Run button - tkinter.Button(code_frame, text="Run PyGeM", command=self._run_simulation, bg='#065893', \ - fg='#f19625', font='bold').grid(row=5, column=0, columnspan=2, padx=3, pady=3) - - # Menu - menubar = tkinter.Menu(self.root) - helpmenu = tkinter.Menu(menubar, tearoff=0) - helpmenu.add_command(label="About...", command=self._goto_website) - menubar.add_cascade(label="Help", menu=helpmenu) - - self.root.config(menu=menubar) - - def start(self): - """ - This method inizializes and starts the GUI. - """ - self._main() - self.root.mainloop() + """ + The class for the Graphic Unit Interface. + + :cvar string filename_geometry: input geometry to be morphed. + :cvar string filename_parameters: input parameters file for FFD. + :cvar int check_var_dump_orig: dump or not the original FFD lattice. + :cvar int check_var_dump_morphed: dump or not the morphed FFD lattice. + :cvar string outfilename: name of the output file geometry. + The extension of the file is set automatically equal to the on of input file 'filename_geometry'. + :cvar string outfilename_lattice_orig: name of the dumped file for the original lattice. + The extension of the file is set automatically equal to '.vtk'. + :cvar string outfilename_lattice_mod: name of the dumped file for the morphed lattice. + The extension of the file is set automatically equal to '.vtk'. + :cvar tkinter.Tk root: main window object of the GUI. + :cvar string print_geometry_path: geometry path to be printed close to the 'pick geometry' button. + :cvar string print_parameter_path: parameters file path to be printed close to the + 'pick parameters' button. + :cvar tkinter.Label label_geo: label related to 'print_geometry_path'. + :cvar tkinter.Label label_params: label related to 'print_parameters_path'. + :cvar string url: url of the github page of PyGeM. + :cvar tkinter.Canvas logo_panel: canvas for PyGeM logo. + :cvar tkinter.PhotoImage img: PyGeM logo. + :cvar tkinter.Frame orig_geo_frame: frame for plotting of the original geometry. + :cvar tkinter.Frame mod_geo_frame: frame for plotting of the final geometry. + """ + + def __init__(self): + self.root = tkinter.Tk() + self.root.resizable(width=False, height=False) + self.root.minsize(width=1400, height=400) + self.root.maxsize(width=1400, height=400) + self.root.title('PyGeM') + self.filename_geometry = tkinter.StringVar() + self.filename_parameters = tkinter.StringVar() + self.check_var_dump_orig = tkinter.IntVar() + self.check_var_dump_morphed = tkinter.IntVar() + self.outfilename = tkinter.StringVar() + self.outfilename_lattice_orig = tkinter.StringVar() + self.outfilename_lattice_mod = tkinter.StringVar() + self.print_geometry_path = tkinter.StringVar() + self.print_parameter_path = tkinter.StringVar() + self.label_geo = None + self.label_params = None + self.url = 'https://github.com/mathLab/PyGeM' + self.logo_panel = None + self.img = None + self.orig_geo_frame = None + self.mod_geo_frame = None + + def _chose_geometry(self): + """ + The private method explores the file system and allows to select the wanted geometry. + Up to now, you can select only IGES, OpenFOAM, STL, UNV or VTK geometry file. + """ + self.filename_geometry = askopenfilename(filetypes=[("IGES File", ('*.iges', '*.igs')), \ + ("OpenFOAM File", '*'), ('STL File', '*.stl'), ('UNV File', '*.unv'), \ + ('VTK File', '*.vtk'), ('STEP File', ('*.step, *.stp')) ('All', '*')]) + self.print_geometry_path.set(self.filename_geometry) + self.label_geo.configure(fg='green') + + def _chose_parameters(self): + """ + The private method explores the file system and allows to select the wanted parameters file. + It visualizes only .prm files. + """ + self.filename_parameters = askopenfilename(filetypes=[("Params File", + "*.prm")]) + self.print_parameter_path.set(self.filename_parameters) + self.label_params.configure(fg='green') + + def _run_simulation(self): + """ + The private method runs the geometrical morphing. + """ + import pygem as pg + params = pg.params.FFDParameters() + params.read_parameters(filename=self.filename_parameters) + + file_extension_in = os.path.splitext(self.filename_geometry)[-1] + ext_handlers = { + '.stl': pg.stlhandler.StlHandler(), + '.iges': pg.igeshandler.IgesHandler(), + '.igs': pg.igeshandler.IgesHandler(), + '.unv': pg.unvhandler.UnvHandler(), + '': pg.openfhandler.OpenFoamHandler(), + '.vtk': pg.vtkhandler.VtkHandler(), + '.stp': pg.stephandler.StepHandler(), + '.step': pg.stephandler.StepHandler(), + } + + if file_extension_in in ext_handlers: + geo_handler = ext_handlers[file_extension_in] + else: + raise NotImplementedError("Format not implemented yet.") + + mesh_points = geo_handler.parse(self.filename_geometry) + free_form = pg.freeform.FFD(params, mesh_points) + free_form.perform() + new_mesh_points = free_form.modified_mesh_points + + geo_handler.write(new_mesh_points, + self.outfilename.get() + file_extension_in) + + if self.check_var_dump_orig.get() == 1: + pg.utils.write_bounding_box( + params, + self.outfilename_lattice_orig.get() + '.vtk', False) + if self.check_var_dump_morphed.get() == 1: + pg.utils.write_bounding_box( + params, + self.outfilename_lattice_mod.get() + '.vtk', True) + + if file_extension_in in ['.vtk', '.stl', '.iges', '.igs']: + figure_in = geo_handler.plot() + figure_in.set_size_inches(4, 3) + FigureCanvasTkAgg(figure_in, master=self.orig_geo_frame).get_tk_widget().grid(row=1, column=0, \ + padx=5, pady=5) + figure_out = geo_handler.plot( + self.outfilename.get() + file_extension_in) + figure_out.set_size_inches(4, 3) + FigureCanvasTkAgg(figure_out, master=self.mod_geo_frame).get_tk_widget().grid(row=1, column=0, \ + padx=5, pady=5) + + def _goto_website(self): + """ + The private method opens the PyGeM main page on github. + It is used for info about PyGeM in the menu. + """ + webbrowser.open(self.url) + + def _main(self): + """ + The private method inizializes and visualizes the window. + """ + self.logo_panel = tkinter.Canvas(self.root, height=60, width=60) + self.logo_panel.pack(side="bottom", padx=5, pady=5, anchor=tkinter.SE) + try: + self.img = tkinter.PhotoImage( + master=self.logo_panel, file='readme/logo_PyGeM_gui.gif') + except: + self.img = tkinter.PhotoImage( + master=self.logo_panel, file='../readme/logo_PyGeM_gui.gif') + self.logo_panel.create_image(35, 35, image=self.img) + + self.orig_geo_frame = tkinter.Frame( + self.root, height=450, width=360, bg='#c1d0f0') + self.orig_geo_frame.pack(side="left", padx=5, pady=5) + self.orig_geo_frame.pack_propagate(0) + tkinter.Label(self.orig_geo_frame, text="INPUT GEOMETRY", bg='#c1d0f0', \ + font=("Arial", 20)).grid(row=0, column=0, padx=3, pady=3) + + self.mod_geo_frame = tkinter.Frame( + self.root, height=450, width=360, bg='#80ff80', padx=5, pady=5) + self.mod_geo_frame.pack(side="right", padx=5, pady=5) + self.mod_geo_frame.pack_propagate(0) + tkinter.Label(self.mod_geo_frame, text="OUTPUT GEOMETRY", bg='#80ff80', \ + font=("Arial", 20)).grid(row=0, column=0, padx=3, pady=3) + + code_frame = tkinter.Frame( + self.root, + height=490, + width=360, + relief=tkinter.GROOVE, + borderwidth=1) + code_frame.pack(padx=5, pady=5) + code_frame.pack_propagate(0) + + # Buttons 1 + tkinter.Button(code_frame, text="Pick the geometry", \ + command=self._chose_geometry).grid(row=0, column=0, padx=3, pady=3) + self.label_geo = tkinter.Label( + code_frame, textvariable=self.print_geometry_path, fg='red') + self.print_geometry_path.set("No geometry chosen!") + self.label_geo.grid(row=0, column=1, padx=3, pady=3) + + # Button 2 + tkinter.Button(code_frame, text="Pick the parameters", \ + command=self._chose_parameters).grid(row=1, column=0, padx=3, pady=3) + self.label_params = tkinter.Label( + code_frame, textvariable=self.print_parameter_path, fg='red') + self.print_parameter_path.set("No parameters file chosen!") + self.label_params.grid(row=1, column=1, padx=3, pady=3) + + # Entry + tkinter.Label( + code_frame, text="Output geometry file").grid( + row=2, column=0, padx=3, pady=3) + tkinter.Entry(code_frame, bd=5, textvariable=self.outfilename).grid(row=2, column=1, \ + padx=3, pady=3) + + # Checkboxes + tkinter.Checkbutton(code_frame, text="Dump Original FFD lattice", \ + variable=self.check_var_dump_orig, onvalue=1, offvalue=0, height=3, width=20).grid(row=3, \ + column=0) + tkinter.Entry( + code_frame, bd=5, textvariable=self.outfilename_lattice_orig).grid( + row=3, column=1) + + tkinter.Checkbutton(code_frame, text="Dump Morphed FFD lattice", \ + variable=self.check_var_dump_morphed, onvalue=1, offvalue=0, height=3, width=20).grid(row=4, \ + column=0) + tkinter.Entry( + code_frame, bd=5, textvariable=self.outfilename_lattice_mod).grid( + row=4, column=1) + + # Run button + tkinter.Button(code_frame, text="Run PyGeM", command=self._run_simulation, bg='#065893', \ + fg='#f19625', font='bold').grid(row=5, column=0, columnspan=2, padx=3, pady=3) + + # Menu + menubar = tkinter.Menu(self.root) + helpmenu = tkinter.Menu(menubar, tearoff=0) + helpmenu.add_command(label="About...", command=self._goto_website) + menubar.add_cascade(label="Help", menu=helpmenu) + + self.root.config(menu=menubar) + + def start(self): + """ + This method inizializes and starts the GUI. + """ + self._main() + self.root.mainloop() if __name__ == "__main__": - app = Gui() - app.start() + app = Gui() + app.start() diff --git a/pygem/idw.py b/pygem/idw.py index 7293cb8..5687972 100644 --- a/pygem/idw.py +++ b/pygem/idw.py @@ -10,32 +10,32 @@ :Theoretical Insight: - This implementation is based on the simplest form of inverse distance - weighting interpolation, proposed by D. Shepard, A two-dimensional - interpolation function for irregularly-spaced data, Proceedings of the 23 rd - ACM National Conference. + This implementation is based on the simplest form of inverse distance + weighting interpolation, proposed by D. Shepard, A two-dimensional + interpolation function for irregularly-spaced data, Proceedings of the 23 rd + ACM National Conference. - The interpolation value :math:`u` of a given point :math:`\\mathrm{x}` - from a set of samples :math:`u_k = u(\\mathrm{x}_k)`, with - :math:`k = 1,2,\dotsc,\\mathcal{N}`, is given by: + The interpolation value :math:`u` of a given point :math:`\\mathrm{x}` + from a set of samples :math:`u_k = u(\\mathrm{x}_k)`, with + :math:`k = 1,2,\dotsc,\\mathcal{N}`, is given by: - .. math:: - u(\\mathrm{x}) = \\displaystyle\\sum_{k=1}^\\mathcal{N} - \\frac{w(\\mathrm{x},\\mathrm{x}_k)} - {\\displaystyle\\sum_{j=1}^\\mathcal{N} w(\\mathrm{x},\\mathrm{x}_j)} - u_k + .. math:: + u(\\mathrm{x}) = \\displaystyle\\sum_{k=1}^\\mathcal{N} + \\frac{w(\\mathrm{x},\\mathrm{x}_k)} + {\\displaystyle\\sum_{j=1}^\\mathcal{N} w(\\mathrm{x},\\mathrm{x}_j)} + u_k - where, in general, :math:`w(\\mathrm{x}, \\mathrm{x}_i)` represents the - weighting function: + where, in general, :math:`w(\\mathrm{x}, \\mathrm{x}_i)` represents the + weighting function: - .. math:: - w(\\mathrm{x}, \\mathrm{x}_i) = \\| \\mathrm{x} - \\mathrm{x}_i \\|^{-p} - - being :math:`\\| \\mathrm{x} - \\mathrm{x}_i \\|^{-p} \\ge 0` is the - Euclidean distance between :math:`\\mathrm{x}` and data point - :math:`\\mathrm{x}_i` and :math:`p` is a power parameter, typically equal to - 2. + .. math:: + w(\\mathrm{x}, \\mathrm{x}_i) = \\| \\mathrm{x} - \\mathrm{x}_i \\|^{-p} + + being :math:`\\| \\mathrm{x} - \\mathrm{x}_i \\|^{-p} \\ge 0` is the + Euclidean distance between :math:`\\mathrm{x}` and data point + :math:`\\mathrm{x}_i` and :math:`p` is a power parameter, typically equal to + 2. """ import numpy as np @@ -44,78 +44,72 @@ class IDW(object): - """ - Class that handles the IDW technique. - - :param idw_parameters: the parameters of the IDW - :type idw_parameters: :class:`IDWParameters` - :param numpy.ndarray original_mesh_points: coordinates of the original - points of the mesh. - - :cvar parameters: the parameters of the IDW. - :vartype parameters: :class:`~pygem.params_idw.IDWParameters` - :cvar numpy.ndarray original_mesh_points: coordinates of the original - points of the mesh. - :cvar numpy.ndarray modified_mesh_points: coordinates of the deformed - points of the mesh. - - :Example: - - >>> from pygem.idw import IDW - >>> from pygem.params_idw import IDWParameters - >>> import numpy as np - >>> params = IDWParameters() - >>> params.read_parameters('tests/test_datasets/parameters_idw_cube.prm') - >>> nx, ny, nz = (20, 20, 20) - >>> mesh = np.zeros((nx * ny * nz, 3)) - >>> xv = np.linspace(0, 1, nx) - >>> yv = np.linspace(0, 1, ny) - >>> zv = np.linspace(0, 1, nz) - >>> z, y, x = np.meshgrid(zv, yv, xv) - >>> mesh = np.array([x.ravel(), y.ravel(), z.ravel()]) - >>> original_mesh_points = mesh.T - >>> idw = IDW(rbf_parameters, original_mesh_points) - >>> idw.perform() - >>> new_mesh_points = idw.modified_mesh_points - """ - - def __init__(self, idw_parameters, original_mesh_points): - self.parameters = idw_parameters - self.original_mesh_points = original_mesh_points - self.modified_mesh_points = None - - def perform(self): - """ - This method performs the deformation of the mesh points. After the - execution it sets `self.modified_mesh_points`. - """ - - def distance(u, v): - return np.linalg.norm(u - v, self.parameters.power) - - # Compute displacement of the control points - displ = ( - self.parameters.deformed_control_points - - self.parameters.original_control_points - ) - - # Compute the distance between the mesh points and the control points - dist = cdist( - self.original_mesh_points, - self.parameters.original_control_points, - distance - ) - - # Weights are set as the reciprocal of the distance if the distance is - # not zero, otherwise 1.0 where distance is zero. - weights = np.zeros(dist.shape) - for i, d in enumerate(dist): - weights[i] = 1. / d if d.all() else np.where(d == 0.0, 1.0, 0.0) - - - offset = np.array([ - np.sum(displ * wi[:, np.newaxis] / np.sum(wi), axis=0) - for wi in weights - ]) - - self.modified_mesh_points = self.original_mesh_points + offset + """ + Class that handles the IDW technique. + + :param idw_parameters: the parameters of the IDW + :type idw_parameters: :class:`IDWParameters` + :param numpy.ndarray original_mesh_points: coordinates of the original + points of the mesh. + + :cvar parameters: the parameters of the IDW. + :vartype parameters: :class:`~pygem.params_idw.IDWParameters` + :cvar numpy.ndarray original_mesh_points: coordinates of the original + points of the mesh. + :cvar numpy.ndarray modified_mesh_points: coordinates of the deformed + points of the mesh. + + :Example: + + >>> from pygem.idw import IDW + >>> from pygem.params_idw import IDWParameters + >>> import numpy as np + >>> params = IDWParameters() + >>> params.read_parameters('tests/test_datasets/parameters_idw_cube.prm') + >>> nx, ny, nz = (20, 20, 20) + >>> mesh = np.zeros((nx * ny * nz, 3)) + >>> xv = np.linspace(0, 1, nx) + >>> yv = np.linspace(0, 1, ny) + >>> zv = np.linspace(0, 1, nz) + >>> z, y, x = np.meshgrid(zv, yv, xv) + >>> mesh = np.array([x.ravel(), y.ravel(), z.ravel()]) + >>> original_mesh_points = mesh.T + >>> idw = IDW(rbf_parameters, original_mesh_points) + >>> idw.perform() + >>> new_mesh_points = idw.modified_mesh_points + """ + + def __init__(self, idw_parameters, original_mesh_points): + self.parameters = idw_parameters + self.original_mesh_points = original_mesh_points + self.modified_mesh_points = None + + def perform(self): + """ + This method performs the deformation of the mesh points. After the + execution it sets `self.modified_mesh_points`. + """ + + def distance(u, v): + return np.linalg.norm(u - v, self.parameters.power) + + # Compute displacement of the control points + displ = (self.parameters.deformed_control_points - + self.parameters.original_control_points) + + # Compute the distance between the mesh points and the control points + dist = cdist(self.original_mesh_points, + self.parameters.original_control_points, distance) + + # Weights are set as the reciprocal of the distance if the distance is + # not zero, otherwise 1.0 where distance is zero. + weights = np.zeros(dist.shape) + for i, d in enumerate(dist): + weights[i] = 1. / d if d.all() else np.where(d == 0.0, 1.0, 0.0) + + offset = np.array([ + np.sum(displ * wi[:, np.newaxis] / np.sum(wi), axis=0) + for wi in weights + ]) + + self.modified_mesh_points = self.original_mesh_points + offset diff --git a/pygem/igeshandler.py b/pygem/igeshandler.py index 43efd06..9508372 100644 --- a/pygem/igeshandler.py +++ b/pygem/igeshandler.py @@ -3,71 +3,71 @@ """ from OCC.IGESControl import (IGESControl_Reader, IGESControl_Writer, - IGESControl_Controller_Init) + IGESControl_Controller_Init) from OCC.IFSelect import IFSelect_RetDone from pygem.nurbshandler import NurbsHandler class IgesHandler(NurbsHandler): - """ - Iges file handler class + """ + Iges file handler class - :cvar string infile: name of the input file to be processed. - :cvar string outfile: name of the output file where to write in. - :cvar list extensions: list of extensions of the input/output files. - It is equal to ['.iges', '.igs']. - :cvar list control_point_position: index of the first NURBS control point (or pole) - of each face of the iges file. - :cvar float tolerance: tolerance for the construction of the faces and wires - in the write function. Default value is 1e-6. - :cvar TopoDS_Shape shape: shape meant for modification. + :cvar string infile: name of the input file to be processed. + :cvar string outfile: name of the output file where to write in. + :cvar list extensions: list of extensions of the input/output files. + It is equal to ['.iges', '.igs']. + :cvar list control_point_position: index of the first NURBS control point (or pole) + of each face of the iges file. + :cvar float tolerance: tolerance for the construction of the faces and wires + in the write function. Default value is 1e-6. + :cvar TopoDS_Shape shape: shape meant for modification. - .. warning:: + .. warning:: - - For non trivial geometries it could be necessary to increase the tolerance. - Linking edges into a single wire and then trimming the surface with the wire - can be hard for the software, especially when the starting CAD has not been - made for analysis but for design purposes. - """ + - For non trivial geometries it could be necessary to increase the tolerance. + Linking edges into a single wire and then trimming the surface with the wire + can be hard for the software, especially when the starting CAD has not been + made for analysis but for design purposes. + """ - def __init__(self): - super(IgesHandler, self).__init__() - self.extensions = ['.iges', '.igs'] + def __init__(self): + super(IgesHandler, self).__init__() + self.extensions = ['.iges', '.igs'] - def load_shape_from_file(self, filename): - """ - This class method loads a shape from the file `filename`. + def load_shape_from_file(self, filename): + """ + This class method loads a shape from the file `filename`. - :param string filename: name of the input file. - It should have proper extension (.iges or .igs) + :param string filename: name of the input file. + It should have proper extension (.iges or .igs) - :return: shape: loaded shape - :rtype: TopoDS_Shape - """ - self._check_filename_type(filename) - self._check_extension(filename) - reader = IGESControl_Reader() - return_reader = reader.ReadFile(filename) - # check status - if return_reader == IFSelect_RetDone: - return_transfer = reader.TransferRoots() - if return_transfer: - # load all shapes in one - shape = reader.OneShape() + :return: shape: loaded shape + :rtype: TopoDS_Shape + """ + self._check_filename_type(filename) + self._check_extension(filename) + reader = IGESControl_Reader() + return_reader = reader.ReadFile(filename) + # check status + if return_reader == IFSelect_RetDone: + return_transfer = reader.TransferRoots() + if return_transfer: + # load all shapes in one + shape = reader.OneShape() - return shape + return shape - def write_shape_to_file(self, shape, filename): - """ - This class method saves the `shape` to the file `filename`. + def write_shape_to_file(self, shape, filename): + """ + This class method saves the `shape` to the file `filename`. - :param: TopoDS_Shape shape: loaded shape - :param string filename: name of the input file. - It should have proper extension (.iges or .igs) - """ - self._check_filename_type(filename) - self._check_extension(filename) - IGESControl_Controller_Init() - writer = IGESControl_Writer() - writer.AddShape(shape) - writer.Write(filename) + :param: TopoDS_Shape shape: loaded shape + :param string filename: name of the input file. + It should have proper extension (.iges or .igs) + """ + self._check_filename_type(filename) + self._check_extension(filename) + IGESControl_Controller_Init() + writer = IGESControl_Writer() + writer.AddShape(shape) + writer.Write(filename) diff --git a/pygem/khandler.py b/pygem/khandler.py index df8d304..544d40d 100644 --- a/pygem/khandler.py +++ b/pygem/khandler.py @@ -14,6 +14,7 @@ class KHandler(fh.FileHandler): :cvar list extensions: extensions of the input/output files. It is equal to '.k'. """ + def __init__(self): super(KHandler, self).__init__() self.extensions = ['.k'] @@ -81,7 +82,8 @@ def write(self, mesh_points, filename): index = -9 else: for j in range(0, 3): - line = line[:8+16*(j)] + '{:16.10f}'.format(mesh_points[i][j]) + line[8+16*(j+1):] + line = line[:8 + 16 * (j)] + '{:16.10f}'.format( + mesh_points[i][j]) + line[8 + 16 * (j + 1):] i += 1 index = num output_file.write(line) diff --git a/pygem/nurbshandler.py b/pygem/nurbshandler.py index 5c06046..d9c8c5e 100644 --- a/pygem/nurbshandler.py +++ b/pygem/nurbshandler.py @@ -8,16 +8,16 @@ import OCC.TopoDS from OCC.BRep import (BRep_Tool, BRep_Builder, BRep_Tool_Curve) from OCC.BRepBuilderAPI import (BRepBuilderAPI_MakeEdge, BRepBuilderAPI_MakeFace, \ - BRepBuilderAPI_NurbsConvert, BRepBuilderAPI_MakeWire, BRepBuilderAPI_Sewing) + BRepBuilderAPI_NurbsConvert, BRepBuilderAPI_MakeWire, BRepBuilderAPI_Sewing) from OCC.Display.SimpleGui import init_display from OCC.GeomConvert import (geomconvert_SurfaceToBSplineSurface, - geomconvert_CurveToBSplineCurve) + geomconvert_CurveToBSplineCurve) from OCC.ShapeFix import (ShapeFix_ShapeTolerance, ShapeFix_Shell) from OCC.ShapeAnalysis import ShapeAnalysis_WireOrder import OCC.Precision from OCC.StlAPI import StlAPI_Writer from OCC.TopAbs import (TopAbs_FACE, TopAbs_EDGE, TopAbs_WIRE, TopAbs_FORWARD, - TopAbs_SHELL) + TopAbs_SHELL) from OCC.TopExp import (TopExp_Explorer, topexp) from OCC.gp import (gp_Pnt, gp_XYZ) from OCC.TColgp import (TColgp_Array1OfPnt, TColgp_Array2OfPnt) @@ -29,230 +29,237 @@ class NurbsHandler(fh.FileHandler): - """ - Nurbs file handler base class - - :cvar string infile: name of the input file to be processed. - :cvar string outfile: name of the output file where to write in. - :cvar list control_point_position: index of the first NURBS control point (or pole) - of each face of the files. - :cvar TopoDS_Shape shape: shape meant for modification. - :cvar float tolerance: tolerance for the construction of the faces and wires - in the write function. Default value is 1e-6. - - .. warning:: - - - For non trivial geometries it could be necessary to increase the tolerance. - Linking edges into a single wire and then trimming the surface with the wire - can be hard for the software, especially when the starting CAD has not been - made for analysis but for design purposes. - """ - - def __init__(self): - super(NurbsHandler, self).__init__() - self._control_point_position = None - self.tolerance = 1e-6 - self.shape = None - self.check_topo = 0 - - def _check_infile_instantiation(self): - """ - This private method checks if `self.infile` and `self.shape' are instantiated. If not it means - that nobody called the parse method and at least one of them is None` If the check fails - it raises a RuntimeError. - - """ - if not self.shape or not self.infile: - raise RuntimeError("You can not write a file without having parsed one.") - - def load_shape_from_file(self, filename): - """ - Abstract method to load a specific file as a shape. - - Not implemented, it has to be implemented in subclasses. - """ - raise NotImplementedError("Subclass must implement abstract method " +\ - self.__class__.__name__ + ".load_shape_from_file") - - def parse(self, filename): - """ - Method to parse the file `filename`. It returns a matrix with all the coordinates. - - :param string filename: name of the input file. - - :return: mesh_points: it is a `n_points`-by-3 matrix containing the coordinates of - the points of the mesh - :rtype: numpy.ndarray - - """ - self.infile = filename - - self.shape = self.load_shape_from_file(filename) - - # cycle on the faces to get the control points - # init some quantities - n_faces = 0 - control_point_position = [0] - faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) - mesh_points = np.zeros(shape=(0, 3)) - - while faces_explorer.More(): - # performing some conversions to get the right format (BSplineSurface) - face = OCC.TopoDS.topods_Face(faces_explorer.Current()) - nurbs_converter = BRepBuilderAPI_NurbsConvert(face) - nurbs_converter.Perform(face) - nurbs_face = nurbs_converter.Shape() - brep_face = BRep_Tool.Surface(OCC.TopoDS.topods_Face(nurbs_face)) - bspline_face = geomconvert_SurfaceToBSplineSurface(brep_face) - - # openCascade object - occ_face = bspline_face.GetObject() - - # extract the Control Points of each face - n_poles_u = occ_face.NbUPoles() - n_poles_v = occ_face.NbVPoles() - control_polygon_coordinates = np.zeros(\ - shape=(n_poles_u * n_poles_v, 3)) - - # cycle over the poles to get their coordinates - i = 0 - for pole_u_direction in range(n_poles_u): - for pole_v_direction in range(n_poles_v): - control_point_coordinates = occ_face.Pole(\ - pole_u_direction + 1, pole_v_direction + 1) - control_polygon_coordinates[i, :] = [control_point_coordinates.X(),\ - control_point_coordinates.Y(),\ - control_point_coordinates.Z()] - i += 1 - # pushing the control points coordinates to the mesh_points array (used for FFD) - mesh_points = np.append(mesh_points, control_polygon_coordinates, axis=0) - control_point_position.append(control_point_position[-1] + n_poles_u * n_poles_v) - - n_faces += 1 - faces_explorer.Next() - self._control_point_position = control_point_position - return mesh_points - - def write(self, mesh_points, filename, tolerance=None): - """ - Writes a output file, called filename, copying all the structures from self.filename but - the coordinates. mesh_points is a matrix that contains the new coordinates to - write in the output file. - - :param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix containing - the coordinates of the points of the mesh - :param string filename: name of the output file. - :param float tolerance: tolerance for the construction of the faces and wires - in the write function. If not given it uses `self.tolerance`. - """ - self._check_filename_type(filename) - self._check_extension(filename) - self._check_infile_instantiation() - - self.outfile = filename - - if tolerance is not None: - self.tolerance = tolerance - - # cycle on the faces to update the control points position - # init some quantities - faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) - n_faces = 0 - control_point_position = self._control_point_position - - compound_builder = BRep_Builder() - compound = OCC.TopoDS.TopoDS_Compound() - compound_builder.MakeCompound(compound) - - while faces_explorer.More(): - # similar to the parser method - face = OCC.TopoDS.topods_Face(faces_explorer.Current()) - nurbs_converter = BRepBuilderAPI_NurbsConvert(face) - nurbs_converter.Perform(face) - nurbs_face = nurbs_converter.Shape() - face_aux = OCC.TopoDS.topods_Face(nurbs_face) - brep_face = BRep_Tool.Surface(OCC.TopoDS.topods_Face(nurbs_face)) - bspline_face = geomconvert_SurfaceToBSplineSurface(brep_face) - occ_face = bspline_face.GetObject() - - n_poles_u = occ_face.NbUPoles() - n_poles_v = occ_face.NbVPoles() - - i = 0 - for pole_u_direction in range(n_poles_u): - for pole_v_direction in range(n_poles_v): - control_point_coordinates = mesh_points[i + control_point_position[n_faces], :] - point_xyz = gp_XYZ(*control_point_coordinates) - - gp_point = gp_Pnt(point_xyz) - occ_face.SetPole(pole_u_direction + 1, pole_v_direction + 1, gp_point) - i += 1 - - # construct the deformed wire for the trimmed surfaces - wire_maker = BRepBuilderAPI_MakeWire() - tol = ShapeFix_ShapeTolerance() - brep = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), self.tolerance).Face() - brep_face = BRep_Tool.Surface(brep) - - # cycle on the edges - edge_explorer = TopExp_Explorer(nurbs_face, TopAbs_EDGE) - while edge_explorer.More(): - edge = OCC.TopoDS.topods_Edge(edge_explorer.Current()) - # edge in the (u,v) coordinates - edge_uv_coordinates = BRep_Tool.CurveOnSurface(edge, face_aux) - # evaluating the new edge: same (u,v) coordinates, but different (x,y,x) ones - edge_phis_coordinates_aux = BRepBuilderAPI_MakeEdge(\ - edge_uv_coordinates[0], brep_face) - edge_phis_coordinates = edge_phis_coordinates_aux.Edge() - tol.SetTolerance(edge_phis_coordinates, self.tolerance) - wire_maker.Add(edge_phis_coordinates) - edge_explorer.Next() - - # grouping the edges in a wire - wire = wire_maker.Wire() - - # trimming the surfaces - brep_surf = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), wire).Shape() - compound_builder.Add(compound, brep_surf) - n_faces += 1 - faces_explorer.Next() - self.write_shape_to_file(compound, self.outfile) - - def check_topology(self): - """ + """ + Nurbs file handler base class + + :cvar string infile: name of the input file to be processed. + :cvar string outfile: name of the output file where to write in. + :cvar list control_point_position: index of the first NURBS control point (or pole) + of each face of the files. + :cvar TopoDS_Shape shape: shape meant for modification. + :cvar float tolerance: tolerance for the construction of the faces and wires + in the write function. Default value is 1e-6. + + .. warning:: + + - For non trivial geometries it could be necessary to increase the tolerance. + Linking edges into a single wire and then trimming the surface with the wire + can be hard for the software, especially when the starting CAD has not been + made for analysis but for design purposes. + """ + + def __init__(self): + super(NurbsHandler, self).__init__() + self._control_point_position = None + self.tolerance = 1e-6 + self.shape = None + self.check_topo = 0 + + def _check_infile_instantiation(self): + """ + This private method checks if `self.infile` and `self.shape' are instantiated. If not it means + that nobody called the parse method and at least one of them is None` If the check fails + it raises a RuntimeError. + + """ + if not self.shape or not self.infile: + raise RuntimeError( + "You can not write a file without having parsed one.") + + def load_shape_from_file(self, filename): + """ + Abstract method to load a specific file as a shape. + + Not implemented, it has to be implemented in subclasses. + """ + raise NotImplementedError("Subclass must implement abstract method " +\ + self.__class__.__name__ + ".load_shape_from_file") + + def parse(self, filename): + """ + Method to parse the file `filename`. It returns a matrix with all the coordinates. + + :param string filename: name of the input file. + + :return: mesh_points: it is a `n_points`-by-3 matrix containing the coordinates of + the points of the mesh + :rtype: numpy.ndarray + + """ + self.infile = filename + + self.shape = self.load_shape_from_file(filename) + + # cycle on the faces to get the control points + # init some quantities + n_faces = 0 + control_point_position = [0] + faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) + mesh_points = np.zeros(shape=(0, 3)) + + while faces_explorer.More(): + # performing some conversions to get the right format (BSplineSurface) + face = OCC.TopoDS.topods_Face(faces_explorer.Current()) + nurbs_converter = BRepBuilderAPI_NurbsConvert(face) + nurbs_converter.Perform(face) + nurbs_face = nurbs_converter.Shape() + brep_face = BRep_Tool.Surface(OCC.TopoDS.topods_Face(nurbs_face)) + bspline_face = geomconvert_SurfaceToBSplineSurface(brep_face) + + # openCascade object + occ_face = bspline_face.GetObject() + + # extract the Control Points of each face + n_poles_u = occ_face.NbUPoles() + n_poles_v = occ_face.NbVPoles() + control_polygon_coordinates = np.zeros(\ + shape=(n_poles_u * n_poles_v, 3)) + + # cycle over the poles to get their coordinates + i = 0 + for pole_u_direction in range(n_poles_u): + for pole_v_direction in range(n_poles_v): + control_point_coordinates = occ_face.Pole(\ + pole_u_direction + 1, pole_v_direction + 1) + control_polygon_coordinates[i, :] = [control_point_coordinates.X(),\ + control_point_coordinates.Y(),\ + control_point_coordinates.Z()] + i += 1 + # pushing the control points coordinates to the mesh_points array (used for FFD) + mesh_points = np.append( + mesh_points, control_polygon_coordinates, axis=0) + control_point_position.append( + control_point_position[-1] + n_poles_u * n_poles_v) + + n_faces += 1 + faces_explorer.Next() + self._control_point_position = control_point_position + return mesh_points + + def write(self, mesh_points, filename, tolerance=None): + """ + Writes a output file, called filename, copying all the structures from self.filename but + the coordinates. mesh_points is a matrix that contains the new coordinates to + write in the output file. + + :param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix containing + the coordinates of the points of the mesh + :param string filename: name of the output file. + :param float tolerance: tolerance for the construction of the faces and wires + in the write function. If not given it uses `self.tolerance`. + """ + self._check_filename_type(filename) + self._check_extension(filename) + self._check_infile_instantiation() + + self.outfile = filename + + if tolerance is not None: + self.tolerance = tolerance + + # cycle on the faces to update the control points position + # init some quantities + faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) + n_faces = 0 + control_point_position = self._control_point_position + + compound_builder = BRep_Builder() + compound = OCC.TopoDS.TopoDS_Compound() + compound_builder.MakeCompound(compound) + + while faces_explorer.More(): + # similar to the parser method + face = OCC.TopoDS.topods_Face(faces_explorer.Current()) + nurbs_converter = BRepBuilderAPI_NurbsConvert(face) + nurbs_converter.Perform(face) + nurbs_face = nurbs_converter.Shape() + face_aux = OCC.TopoDS.topods_Face(nurbs_face) + brep_face = BRep_Tool.Surface(OCC.TopoDS.topods_Face(nurbs_face)) + bspline_face = geomconvert_SurfaceToBSplineSurface(brep_face) + occ_face = bspline_face.GetObject() + + n_poles_u = occ_face.NbUPoles() + n_poles_v = occ_face.NbVPoles() + + i = 0 + for pole_u_direction in range(n_poles_u): + for pole_v_direction in range(n_poles_v): + control_point_coordinates = mesh_points[ + i + control_point_position[n_faces], :] + point_xyz = gp_XYZ(*control_point_coordinates) + + gp_point = gp_Pnt(point_xyz) + occ_face.SetPole(pole_u_direction + 1, pole_v_direction + 1, + gp_point) + i += 1 + + # construct the deformed wire for the trimmed surfaces + wire_maker = BRepBuilderAPI_MakeWire() + tol = ShapeFix_ShapeTolerance() + brep = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), + self.tolerance).Face() + brep_face = BRep_Tool.Surface(brep) + + # cycle on the edges + edge_explorer = TopExp_Explorer(nurbs_face, TopAbs_EDGE) + while edge_explorer.More(): + edge = OCC.TopoDS.topods_Edge(edge_explorer.Current()) + # edge in the (u,v) coordinates + edge_uv_coordinates = BRep_Tool.CurveOnSurface(edge, face_aux) + # evaluating the new edge: same (u,v) coordinates, but different (x,y,x) ones + edge_phis_coordinates_aux = BRepBuilderAPI_MakeEdge(\ + edge_uv_coordinates[0], brep_face) + edge_phis_coordinates = edge_phis_coordinates_aux.Edge() + tol.SetTolerance(edge_phis_coordinates, self.tolerance) + wire_maker.Add(edge_phis_coordinates) + edge_explorer.Next() + + # grouping the edges in a wire + wire = wire_maker.Wire() + + # trimming the surfaces + brep_surf = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), + wire).Shape() + compound_builder.Add(compound, brep_surf) + n_faces += 1 + faces_explorer.Next() + self.write_shape_to_file(compound, self.outfile) + + def check_topology(self): + """ Method to check the topology of imported geometry. :return: 0: 1 solid = 1 shell = n faces 1: 1 solid = 0 shell = n free faces 2: 1 solid = n shell = n faces (1 shell = 1 face) """ - # read shells and faces - shells_explorer = TopExp_Explorer(self.shape, TopAbs_SHELL) - n_shells = 0 - while shells_explorer.More(): - n_shells += 1 - shells_explorer.Next() - - faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) - n_faces = 0 - while faces_explorer.More(): - n_faces += 1 - faces_explorer.Next() - - print("##############################################\n" - "Model statistics -- Nb Shells: {0} Faces: {1} \n" - "----------------------------------------------\n" - .format(n_shells, n_faces)) - - if n_shells == 0: - self.check_topo = 1 - elif n_shells == n_faces: - self.check_topo = 2 - else: - self.check_topo = 0 - - @staticmethod - def parse_face(topo_face): - """ + # read shells and faces + shells_explorer = TopExp_Explorer(self.shape, TopAbs_SHELL) + n_shells = 0 + while shells_explorer.More(): + n_shells += 1 + shells_explorer.Next() + + faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) + n_faces = 0 + while faces_explorer.More(): + n_faces += 1 + faces_explorer.Next() + + print("##############################################\n" + "Model statistics -- Nb Shells: {0} Faces: {1} \n" + "----------------------------------------------\n".format( + n_shells, n_faces)) + + if n_shells == 0: + self.check_topo = 1 + elif n_shells == n_faces: + self.check_topo = 2 + else: + self.check_topo = 0 + + @staticmethod + def parse_face(topo_face): + """ Method to parse a single Face (a single patch nurbs surface). It returns a matrix with all the coordinates of control points of the Face and a second list with all the control points related to the @@ -268,74 +275,75 @@ def parse_face(topo_face): :rtype: tuple(numpy.ndarray, list) """ - # get some Face - Edge - Vertex data map information - mesh_points_edge = [] - face_exp_wire = TopExp_Explorer(topo_face, TopAbs_WIRE) - # loop on wires per face - while face_exp_wire.More(): - twire = OCC.TopoDS.topods_Wire(face_exp_wire.Current()) - wire_exp_edge = TopExp_Explorer(twire, TopAbs_EDGE) - # loop on edges per wire - while wire_exp_edge.More(): - edge = OCC.TopoDS.topods_Edge(wire_exp_edge.Current()) - bspline_converter = BRepBuilderAPI_NurbsConvert(edge) - bspline_converter.Perform(edge) - bspline_tshape_edge = bspline_converter.Shape() - h_geom_edge, a, b = BRep_Tool_Curve(OCC.TopoDS.topods_Edge( - bspline_tshape_edge)) - h_bspline_edge = geomconvert_CurveToBSplineCurve(h_geom_edge) - bspline_geom_edge = h_bspline_edge.GetObject() - - nb_poles = bspline_geom_edge.NbPoles() - - # Edge geometric properties - edge_ctrlpts = TColgp_Array1OfPnt(1, nb_poles) - bspline_geom_edge.Poles(edge_ctrlpts) - - points_single_edge = np.zeros((0, 3)) - for i in range(1, nb_poles + 1): - ctrlpt = edge_ctrlpts.Value(i) - ctrlpt_position = np.array([[ctrlpt.Coord(1), - ctrlpt.Coord(2), - ctrlpt.Coord(3)]]) - points_single_edge = np.append(points_single_edge, - ctrlpt_position, - axis=0) - - mesh_points_edge.append(points_single_edge) - - wire_exp_edge.Next() - - face_exp_wire.Next() - # extract mesh points (control points) on Face - mesh_points_face = np.zeros((0, 3)) - # convert Face to Geom B-spline Face - nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_face) - nurbs_converter.Perform(topo_face) - nurbs_face = nurbs_converter.Shape() - h_geomsurface = BRep_Tool.Surface(OCC.TopoDS.topods.Face(nurbs_face)) - h_bsurface = geomconvert_SurfaceToBSplineSurface(h_geomsurface) - bsurface = h_bsurface.GetObject() - - # get access to control points (poles) - nb_u = bsurface.NbUPoles() - nb_v = bsurface.NbVPoles() - ctrlpts = TColgp_Array2OfPnt(1, nb_u, 1, nb_v) - bsurface.Poles(ctrlpts) - - for indice_u_direction in range(1, nb_u + 1): - for indice_v_direction in range(1, nb_v + 1): - ctrlpt = ctrlpts.Value(indice_u_direction, indice_v_direction) - ctrlpt_position = np.array([[ctrlpt.Coord(1), - ctrlpt.Coord(2), - ctrlpt.Coord(3)]]) - mesh_points_face = np.append(mesh_points_face, - ctrlpt_position, axis=0) - - return mesh_points_face, mesh_points_edge - - def parse_shape(self, filename): - """ + # get some Face - Edge - Vertex data map information + mesh_points_edge = [] + face_exp_wire = TopExp_Explorer(topo_face, TopAbs_WIRE) + # loop on wires per face + while face_exp_wire.More(): + twire = OCC.TopoDS.topods_Wire(face_exp_wire.Current()) + wire_exp_edge = TopExp_Explorer(twire, TopAbs_EDGE) + # loop on edges per wire + while wire_exp_edge.More(): + edge = OCC.TopoDS.topods_Edge(wire_exp_edge.Current()) + bspline_converter = BRepBuilderAPI_NurbsConvert(edge) + bspline_converter.Perform(edge) + bspline_tshape_edge = bspline_converter.Shape() + h_geom_edge, a, b = BRep_Tool_Curve( + OCC.TopoDS.topods_Edge(bspline_tshape_edge)) + h_bspline_edge = geomconvert_CurveToBSplineCurve(h_geom_edge) + bspline_geom_edge = h_bspline_edge.GetObject() + + nb_poles = bspline_geom_edge.NbPoles() + + # Edge geometric properties + edge_ctrlpts = TColgp_Array1OfPnt(1, nb_poles) + bspline_geom_edge.Poles(edge_ctrlpts) + + points_single_edge = np.zeros((0, 3)) + for i in range(1, nb_poles + 1): + ctrlpt = edge_ctrlpts.Value(i) + ctrlpt_position = np.array( + [[ctrlpt.Coord(1), + ctrlpt.Coord(2), + ctrlpt.Coord(3)]]) + points_single_edge = np.append( + points_single_edge, ctrlpt_position, axis=0) + + mesh_points_edge.append(points_single_edge) + + wire_exp_edge.Next() + + face_exp_wire.Next() + # extract mesh points (control points) on Face + mesh_points_face = np.zeros((0, 3)) + # convert Face to Geom B-spline Face + nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_face) + nurbs_converter.Perform(topo_face) + nurbs_face = nurbs_converter.Shape() + h_geomsurface = BRep_Tool.Surface(OCC.TopoDS.topods.Face(nurbs_face)) + h_bsurface = geomconvert_SurfaceToBSplineSurface(h_geomsurface) + bsurface = h_bsurface.GetObject() + + # get access to control points (poles) + nb_u = bsurface.NbUPoles() + nb_v = bsurface.NbVPoles() + ctrlpts = TColgp_Array2OfPnt(1, nb_u, 1, nb_v) + bsurface.Poles(ctrlpts) + + for indice_u_direction in range(1, nb_u + 1): + for indice_v_direction in range(1, nb_v + 1): + ctrlpt = ctrlpts.Value(indice_u_direction, indice_v_direction) + ctrlpt_position = np.array( + [[ctrlpt.Coord(1), + ctrlpt.Coord(2), + ctrlpt.Coord(3)]]) + mesh_points_face = np.append( + mesh_points_face, ctrlpt_position, axis=0) + + return mesh_points_face, mesh_points_edge + + def parse_shape(self, filename): + """ Method to parse a Shape with multiple objects (1 compound = multi-shells and 1 shell = multi-faces) It returns a list of matrix with all the coordinates of control points @@ -350,57 +358,57 @@ def parse_shape(self, filename): :rtype: a list of shells """ - self.infile = filename - self.shape = self.load_shape_from_file(filename) + self.infile = filename + self.shape = self.load_shape_from_file(filename) - self.check_topology() + self.check_topology() - # parse and get control points - l_shells = [] # an empty list of shells - n_shells = 0 + # parse and get control points + l_shells = [] # an empty list of shells + n_shells = 0 - if self.check_topo == 0: + if self.check_topo == 0: - shells_explorer = TopExp_Explorer(self.shape, TopAbs_SHELL) + shells_explorer = TopExp_Explorer(self.shape, TopAbs_SHELL) - # cycle on shells - while shells_explorer.More(): - topo_shell = OCC.TopoDS.topods.Shell(shells_explorer.Current()) - shell_faces_explorer = TopExp_Explorer(topo_shell, TopAbs_FACE) - l_faces = [] # an empty list of faces per shell + # cycle on shells + while shells_explorer.More(): + topo_shell = OCC.TopoDS.topods.Shell(shells_explorer.Current()) + shell_faces_explorer = TopExp_Explorer(topo_shell, TopAbs_FACE) + l_faces = [] # an empty list of faces per shell - # cycle on faces - while shell_faces_explorer.More(): - topo_face = OCC.TopoDS.topods.Face(shell_faces_explorer - .Current()) - mesh_point, edge_point = self.parse_face(topo_face) - l_faces.append((mesh_point, edge_point)) - shell_faces_explorer.Next() + # cycle on faces + while shell_faces_explorer.More(): + topo_face = OCC.TopoDS.topods.Face( + shell_faces_explorer.Current()) + mesh_point, edge_point = self.parse_face(topo_face) + l_faces.append((mesh_point, edge_point)) + shell_faces_explorer.Next() - l_shells.append(l_faces) - n_shells += 1 - shells_explorer.Next() + l_shells.append(l_faces) + n_shells += 1 + shells_explorer.Next() - else: - # cycle only on faces - shell_faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) - l_faces = [] # an empty list of faces per shell + else: + # cycle only on faces + shell_faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) + l_faces = [] # an empty list of faces per shell - while shell_faces_explorer.More(): - topo_face = OCC.TopoDS.topods.Face(shell_faces_explorer - .Current()) - mesh_point, edge_point = self.parse_face(topo_face) - l_faces.append((mesh_point, edge_point)) - shell_faces_explorer.Next() + while shell_faces_explorer.More(): + topo_face = OCC.TopoDS.topods.Face( + shell_faces_explorer.Current()) + mesh_point, edge_point = self.parse_face(topo_face) + l_faces.append((mesh_point, edge_point)) + shell_faces_explorer.Next() - l_shells.append(l_faces) - n_shells += 1 + l_shells.append(l_faces) + n_shells += 1 - return l_shells + return l_shells - @staticmethod - def write_edge(points_edge, topo_edge): - """ + @staticmethod + def write_edge(points_edge, topo_edge): + """ Method to recreate an Edge associated to a geometric curve after the modification of its points. :param points_edge: the deformed points array. @@ -410,33 +418,33 @@ def write_edge(points_edge, topo_edge): :rtype: TopoDS_Edge """ - # convert Edge to Geom B-spline Curve - nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_edge) - nurbs_converter.Perform(topo_edge) - nurbs_curve = nurbs_converter.Shape() - topo_curve = OCC.TopoDS.topods_Edge(nurbs_curve) - h_geomcurve, param_min, param_max = BRep_Tool.Curve(topo_curve) - h_bcurve = geomconvert_CurveToBSplineCurve(h_geomcurve) - bspline_edge_curve = h_bcurve.GetObject() - - # Edge geometric properties - nb_cpt = bspline_edge_curve.NbPoles() - # check consistency - if points_edge.shape[0] != nb_cpt: - raise ValueError("Input control points do not have not have the " - "same number as the geometric edge!") - - else: - for i in range(1, nb_cpt + 1): - cpt = points_edge[i - 1] - bspline_edge_curve.SetPole(i, gp_Pnt(cpt[0], cpt[1], cpt[2])) - - new_edge = BRepBuilderAPI_MakeEdge(bspline_edge_curve.GetHandle()) - - return new_edge.Edge() - - def write_face(self, points_face, list_points_edge, topo_face, toledge): - """ + # convert Edge to Geom B-spline Curve + nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_edge) + nurbs_converter.Perform(topo_edge) + nurbs_curve = nurbs_converter.Shape() + topo_curve = OCC.TopoDS.topods_Edge(nurbs_curve) + h_geomcurve, param_min, param_max = BRep_Tool.Curve(topo_curve) + h_bcurve = geomconvert_CurveToBSplineCurve(h_geomcurve) + bspline_edge_curve = h_bcurve.GetObject() + + # Edge geometric properties + nb_cpt = bspline_edge_curve.NbPoles() + # check consistency + if points_edge.shape[0] != nb_cpt: + raise ValueError("Input control points do not have not have the " + "same number as the geometric edge!") + + else: + for i in range(1, nb_cpt + 1): + cpt = points_edge[i - 1] + bspline_edge_curve.SetPole(i, gp_Pnt(cpt[0], cpt[1], cpt[2])) + + new_edge = BRepBuilderAPI_MakeEdge(bspline_edge_curve.GetHandle()) + + return new_edge.Edge() + + def write_face(self, points_face, list_points_edge, topo_face, toledge): + """ Method to recreate a Face associated to a geometric surface after the modification of Face points. It returns a TopoDS_Face. @@ -450,153 +458,152 @@ def write_face(self, points_face, list_points_edge, topo_face, toledge): """ - # convert Face to Geom B-spline Surface - nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_face) - nurbs_converter.Perform(topo_face) - nurbs_face = nurbs_converter.Shape() - topo_nurbsface = OCC.TopoDS.topods.Face(nurbs_face) - h_geomsurface = BRep_Tool.Surface(topo_nurbsface) - h_bsurface = geomconvert_SurfaceToBSplineSurface(h_geomsurface) - bsurface = h_bsurface.GetObject() - - nb_u = bsurface.NbUPoles() - nb_v = bsurface.NbVPoles() - # check consistency - if points_face.shape[0] != nb_u * nb_v: - raise ValueError("Input control points do not have not have the " - "same number as the geometric face!") - - # cycle on the face points - indice_cpt = 0 - for iu in range(1, nb_u + 1): - for iv in range(1, nb_v + 1): - cpt = points_face[indice_cpt] - bsurface.SetPole(iu, iv, gp_Pnt(cpt[0], cpt[1], cpt[2])) - indice_cpt += 1 - - # create modified new face - new_bspline_tface = BRepBuilderAPI_MakeFace() - toler = OCC.Precision.precision_Confusion() - new_bspline_tface.Init(bsurface.GetHandle(), False, toler) - - # cycle on the wires - face_wires_explorer = TopExp_Explorer(topo_nurbsface - .Oriented(TopAbs_FORWARD), - TopAbs_WIRE) - ind_edge_total = 0 - - while face_wires_explorer.More(): - # get old wire - twire = OCC.TopoDS.topods_Wire(face_wires_explorer.Current()) - - # cycle on the edges - ind_edge = 0 - wire_explorer_edge = TopExp_Explorer(twire.Oriented(TopAbs_FORWARD), - TopAbs_EDGE) - # check edges order on the wire - mode3d = True - tolerance_edges = toledge - - wire_order = ShapeAnalysis_WireOrder(mode3d, tolerance_edges) - # an edge list - deformed_edges = [] - # cycle on the edges - while wire_explorer_edge.More(): - tedge = OCC.TopoDS.topods_Edge(wire_explorer_edge.Current()) - new_bspline_tedge = self.write_edge( - list_points_edge[ind_edge_total], tedge) - - deformed_edges.append(new_bspline_tedge) - analyzer = topexp() - vfirst = analyzer.FirstVertex(new_bspline_tedge) - vlast = analyzer.LastVertex(new_bspline_tedge) - pt1 = BRep_Tool.Pnt(vfirst) - pt2 = BRep_Tool.Pnt(vlast) - - wire_order.Add(pt1.XYZ(), pt2.XYZ()) - - ind_edge += 1 - ind_edge_total += 1 - wire_explorer_edge.Next() - - # grouping the edges in a wire, then in the face - # check edges order and connectivity within the wire - wire_order.Perform() - # new wire to be created - stol = ShapeFix_ShapeTolerance() - new_bspline_twire = BRepBuilderAPI_MakeWire() - for order_i in range(1, wire_order.NbEdges() + 1): - deformed_edge_i = wire_order.Ordered(order_i) - if deformed_edge_i > 0: - # insert the deformed edge to the new wire - new_edge_toadd = deformed_edges[deformed_edge_i - 1] - stol.SetTolerance(new_edge_toadd, toledge) - new_bspline_twire.Add(new_edge_toadd) - if new_bspline_twire.Error() != 0: - stol.SetTolerance(new_edge_toadd, toledge * 10.0) - new_bspline_twire.Add(new_edge_toadd) - else: - deformed_edge_revers = deformed_edges[ - np.abs(deformed_edge_i) - 1] - stol.SetTolerance(deformed_edge_revers, toledge) - new_bspline_twire.Add(deformed_edge_revers) - if new_bspline_twire.Error() != 0: - stol.SetTolerance(deformed_edge_revers, toledge * 10.0) - new_bspline_twire.Add(deformed_edge_revers) - # add new wire to the Face - new_bspline_tface.Add(new_bspline_twire.Wire()) - face_wires_explorer.Next() - - return OCC.TopoDS.topods.Face(new_bspline_tface.Face()) - - @staticmethod - def combine_faces(compshape, sew_tolerance): - """ + # convert Face to Geom B-spline Surface + nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_face) + nurbs_converter.Perform(topo_face) + nurbs_face = nurbs_converter.Shape() + topo_nurbsface = OCC.TopoDS.topods.Face(nurbs_face) + h_geomsurface = BRep_Tool.Surface(topo_nurbsface) + h_bsurface = geomconvert_SurfaceToBSplineSurface(h_geomsurface) + bsurface = h_bsurface.GetObject() + + nb_u = bsurface.NbUPoles() + nb_v = bsurface.NbVPoles() + # check consistency + if points_face.shape[0] != nb_u * nb_v: + raise ValueError("Input control points do not have not have the " + "same number as the geometric face!") + + # cycle on the face points + indice_cpt = 0 + for iu in range(1, nb_u + 1): + for iv in range(1, nb_v + 1): + cpt = points_face[indice_cpt] + bsurface.SetPole(iu, iv, gp_Pnt(cpt[0], cpt[1], cpt[2])) + indice_cpt += 1 + + # create modified new face + new_bspline_tface = BRepBuilderAPI_MakeFace() + toler = OCC.Precision.precision_Confusion() + new_bspline_tface.Init(bsurface.GetHandle(), False, toler) + + # cycle on the wires + face_wires_explorer = TopExp_Explorer( + topo_nurbsface.Oriented(TopAbs_FORWARD), TopAbs_WIRE) + ind_edge_total = 0 + + while face_wires_explorer.More(): + # get old wire + twire = OCC.TopoDS.topods_Wire(face_wires_explorer.Current()) + + # cycle on the edges + ind_edge = 0 + wire_explorer_edge = TopExp_Explorer( + twire.Oriented(TopAbs_FORWARD), TopAbs_EDGE) + # check edges order on the wire + mode3d = True + tolerance_edges = toledge + + wire_order = ShapeAnalysis_WireOrder(mode3d, tolerance_edges) + # an edge list + deformed_edges = [] + # cycle on the edges + while wire_explorer_edge.More(): + tedge = OCC.TopoDS.topods_Edge(wire_explorer_edge.Current()) + new_bspline_tedge = self.write_edge( + list_points_edge[ind_edge_total], tedge) + + deformed_edges.append(new_bspline_tedge) + analyzer = topexp() + vfirst = analyzer.FirstVertex(new_bspline_tedge) + vlast = analyzer.LastVertex(new_bspline_tedge) + pt1 = BRep_Tool.Pnt(vfirst) + pt2 = BRep_Tool.Pnt(vlast) + + wire_order.Add(pt1.XYZ(), pt2.XYZ()) + + ind_edge += 1 + ind_edge_total += 1 + wire_explorer_edge.Next() + + # grouping the edges in a wire, then in the face + # check edges order and connectivity within the wire + wire_order.Perform() + # new wire to be created + stol = ShapeFix_ShapeTolerance() + new_bspline_twire = BRepBuilderAPI_MakeWire() + for order_i in range(1, wire_order.NbEdges() + 1): + deformed_edge_i = wire_order.Ordered(order_i) + if deformed_edge_i > 0: + # insert the deformed edge to the new wire + new_edge_toadd = deformed_edges[deformed_edge_i - 1] + stol.SetTolerance(new_edge_toadd, toledge) + new_bspline_twire.Add(new_edge_toadd) + if new_bspline_twire.Error() != 0: + stol.SetTolerance(new_edge_toadd, toledge * 10.0) + new_bspline_twire.Add(new_edge_toadd) + else: + deformed_edge_revers = deformed_edges[ + np.abs(deformed_edge_i) - 1] + stol.SetTolerance(deformed_edge_revers, toledge) + new_bspline_twire.Add(deformed_edge_revers) + if new_bspline_twire.Error() != 0: + stol.SetTolerance(deformed_edge_revers, toledge * 10.0) + new_bspline_twire.Add(deformed_edge_revers) + # add new wire to the Face + new_bspline_tface.Add(new_bspline_twire.Wire()) + face_wires_explorer.Next() + + return OCC.TopoDS.topods.Face(new_bspline_tface.Face()) + + @staticmethod + def combine_faces(compshape, sew_tolerance): + """ Method to combine faces in a shell by adding connectivity and continuity :param compshape: TopoDS_Shape :param sew_tolerance: tolerance for sewing :return: Topo_Shell """ - offsew = BRepOffsetAPI_FindContigousEdges(sew_tolerance) - sew = BRepBuilderAPI_Sewing(sew_tolerance) - - face_explorers = TopExp_Explorer(compshape, TopAbs_FACE) - n_faces = 0 - # cycle on Faces - while face_explorers.More(): - tface = OCC.TopoDS.topods.Face(face_explorers.Current()) - sew.Add(tface) - offsew.Add(tface) - n_faces += 1 - face_explorers.Next() - - offsew.Perform() - offsew.Dump() - sew.Perform() - shell = sew.SewedShape() - sew.Dump() - - shell = OCC.TopoDS.topods.Shell(shell) - shell_fixer = ShapeFix_Shell() - shell_fixer.FixFaceOrientation(shell) - - if shell_fixer.Perform(): - print("{} shells fixed! ".format(shell_fixer.NbShells())) - else: - print "Shells not fixed! " - - new_shell = shell_fixer.Shell() - - if OCC.BRepAlgo.brepalgo_IsValid(new_shell): - print "Shell valid! " - else: - print "Shell failed! " - - return new_shell - - def write_shape(self, l_shells, filename, tol): - """ + offsew = BRepOffsetAPI_FindContigousEdges(sew_tolerance) + sew = BRepBuilderAPI_Sewing(sew_tolerance) + + face_explorers = TopExp_Explorer(compshape, TopAbs_FACE) + n_faces = 0 + # cycle on Faces + while face_explorers.More(): + tface = OCC.TopoDS.topods.Face(face_explorers.Current()) + sew.Add(tface) + offsew.Add(tface) + n_faces += 1 + face_explorers.Next() + + offsew.Perform() + offsew.Dump() + sew.Perform() + shell = sew.SewedShape() + sew.Dump() + + shell = OCC.TopoDS.topods.Shell(shell) + shell_fixer = ShapeFix_Shell() + shell_fixer.FixFaceOrientation(shell) + + if shell_fixer.Perform(): + print("{} shells fixed! ".format(shell_fixer.NbShells())) + else: + print "Shells not fixed! " + + new_shell = shell_fixer.Shell() + + if OCC.BRepAlgo.brepalgo_IsValid(new_shell): + print "Shell valid! " + else: + print "Shell failed! " + + return new_shell + + def write_shape(self, l_shells, filename, tol): + """ Method to recreate a TopoDS_Shape associated to a geometric shape after the modification of points of each Face. It returns a TopoDS_Shape (Shape). @@ -607,171 +614,168 @@ def write_shape(self, l_shells, filename, tol): :return: None """ - self.outfile = filename - # global compound containing multiple shells - global_compound_builder = BRep_Builder() - global_comp = OCC.TopoDS.TopoDS_Compound() - global_compound_builder.MakeCompound(global_comp) - - if self.check_topo == 0: - # cycle on shells (multiple objects) - shape_shells_explorer = TopExp_Explorer(self.shape - .Oriented(TopAbs_FORWARD), - TopAbs_SHELL) - ishell = 0 - - while shape_shells_explorer.More(): - per_shell = OCC.TopoDS.topods_Shell(shape_shells_explorer - .Current()) - # a local compound containing a shell - compound_builder = BRep_Builder() - comp = OCC.TopoDS.TopoDS_Compound() - compound_builder.MakeCompound(comp) - - # cycle on faces - faces_explorer = TopExp_Explorer(per_shell - .Oriented(TopAbs_FORWARD), - TopAbs_FACE) - iface = 0 - while faces_explorer.More(): - topoface = OCC.TopoDS.topods.Face(faces_explorer.Current()) - newface = self.write_face(l_shells[ishell][iface][0], - l_shells[ishell][iface][1], - topoface, tol) - - # add face to compound - compound_builder.Add(comp, newface) - iface += 1 - faces_explorer.Next() - - new_shell = self.combine_faces(comp, 0.01) - itype = OCC.TopoDS.TopoDS_Shape.ShapeType(new_shell) - # add the new shell to the global compound - global_compound_builder.Add(global_comp, new_shell) - - print("Shell {0} of type {1} Processed ".format(ishell, itype)) - print "==============================================" - - ishell += 1 - shape_shells_explorer.Next() - - else: - # cycle on faces - # a local compound containing a shell - compound_builder = BRep_Builder() - comp = OCC.TopoDS.TopoDS_Compound() - compound_builder.MakeCompound(comp) - - # cycle on faces - faces_explorer = TopExp_Explorer( - self.shape.Oriented(TopAbs_FORWARD), - TopAbs_FACE) - iface = 0 - while faces_explorer.More(): - topoface = OCC.TopoDS.topods.Face(faces_explorer.Current()) - newface = self.write_face(l_shells[0][iface][0], - l_shells[0][iface][1], - topoface, tol) - - # add face to compound - compound_builder.Add(comp, newface) - iface += 1 - faces_explorer.Next() - - new_shell = self.combine_faces(comp, 0.01) - itype = OCC.TopoDS.TopoDS_Shape.ShapeType(new_shell) - # add the new shell to the global compound - global_compound_builder.Add(global_comp, new_shell) - - print("Shell {0} of type {1} Processed ".format(0, itype)) - print "==============================================" - - self.write_shape_to_file(global_comp, self.outfile) - - def write_shape_to_file(self, shape, filename): - """ - Abstract method to write the 'shape' to the `filename`. - - Not implemented, it has to be implemented in subclasses. - """ - raise NotImplementedError(\ - "Subclass must implement abstract method " +\ - self.__class__.__name__ + ".write_shape_to_file") - - def plot(self, plot_file=None, save_fig=False): - """ - Method to plot a file. If `plot_file` is not given it plots `self.shape`. - - :param string plot_file: the filename you want to plot. - :param bool save_fig: a flag to save the figure in png or not. If True the - plot is not shown. - - :return: figure: matlplotlib structure for the figure of the chosen geometry - :rtype: matplotlib.pyplot.figure - """ - if plot_file is None: - shape = self.shape - plot_file = self.infile - else: - shape = self.load_shape_from_file(plot_file) - - stl_writer = StlAPI_Writer() - # Do not switch SetASCIIMode() from False to True. - stl_writer.SetASCIIMode(False) - stl_writer.Write(shape, 'aux_figure.stl') - - # Create a new plot - figure = pyplot.figure() - axes = mplot3d.Axes3D(figure) - - # Load the STL files and add the vectors to the plot - stl_mesh = mesh.Mesh.from_file('aux_figure.stl') - os.remove('aux_figure.stl') - axes.add_collection3d(mplot3d.art3d.Poly3DCollection(stl_mesh.vectors / 1000)) - - # Get the limits of the axis and center the geometry - max_dim = np.array([\ - np.max(stl_mesh.vectors[:, :, 0]) / 1000,\ - np.max(stl_mesh.vectors[:, :, 1]) / 1000,\ - np.max(stl_mesh.vectors[:, :, 2]) / 1000]) - min_dim = np.array([\ - np.min(stl_mesh.vectors[:, :, 0]) / 1000,\ - np.min(stl_mesh.vectors[:, :, 1]) / 1000,\ - np.min(stl_mesh.vectors[:, :, 2]) / 1000]) - - max_lenght = np.max(max_dim - min_dim) - axes.set_xlim(\ - -.6 * max_lenght + (max_dim[0] + min_dim[0]) / 2,\ - .6 * max_lenght + (max_dim[0] + min_dim[0]) / 2) - axes.set_ylim(\ - -.6 * max_lenght + (max_dim[1] + min_dim[1]) / 2,\ - .6 * max_lenght + (max_dim[1] + min_dim[1]) / 2) - axes.set_zlim(\ - -.6 * max_lenght + (max_dim[2] + min_dim[2]) / 2,\ - .6 * max_lenght + (max_dim[2] + min_dim[2]) / 2) - - # Show the plot to the screen - if not save_fig: - pyplot.show() - else: - figure.savefig(plot_file.split('.')[0] + '.png') - - return figure - - def show(self, show_file=None): - """ - Method to show a file. If `show_file` is not given it plots `self.shape`. - - :param string show_file: the filename you want to show. - """ - if show_file is None: - shape = self.shape - else: - shape = self.load_shape_from_file(show_file) - - display, start_display, __, __ = init_display() - display.FitAll() - display.DisplayShape(shape, update=True) - - # Show the plot to the screen - start_display() + self.outfile = filename + # global compound containing multiple shells + global_compound_builder = BRep_Builder() + global_comp = OCC.TopoDS.TopoDS_Compound() + global_compound_builder.MakeCompound(global_comp) + + if self.check_topo == 0: + # cycle on shells (multiple objects) + shape_shells_explorer = TopExp_Explorer( + self.shape.Oriented(TopAbs_FORWARD), TopAbs_SHELL) + ishell = 0 + + while shape_shells_explorer.More(): + per_shell = OCC.TopoDS.topods_Shell( + shape_shells_explorer.Current()) + # a local compound containing a shell + compound_builder = BRep_Builder() + comp = OCC.TopoDS.TopoDS_Compound() + compound_builder.MakeCompound(comp) + + # cycle on faces + faces_explorer = TopExp_Explorer( + per_shell.Oriented(TopAbs_FORWARD), TopAbs_FACE) + iface = 0 + while faces_explorer.More(): + topoface = OCC.TopoDS.topods.Face(faces_explorer.Current()) + newface = self.write_face(l_shells[ishell][iface][0], + l_shells[ishell][iface][1], + topoface, tol) + + # add face to compound + compound_builder.Add(comp, newface) + iface += 1 + faces_explorer.Next() + + new_shell = self.combine_faces(comp, 0.01) + itype = OCC.TopoDS.TopoDS_Shape.ShapeType(new_shell) + # add the new shell to the global compound + global_compound_builder.Add(global_comp, new_shell) + + print("Shell {0} of type {1} Processed ".format(ishell, itype)) + print "==============================================" + + ishell += 1 + shape_shells_explorer.Next() + + else: + # cycle on faces + # a local compound containing a shell + compound_builder = BRep_Builder() + comp = OCC.TopoDS.TopoDS_Compound() + compound_builder.MakeCompound(comp) + + # cycle on faces + faces_explorer = TopExp_Explorer( + self.shape.Oriented(TopAbs_FORWARD), TopAbs_FACE) + iface = 0 + while faces_explorer.More(): + topoface = OCC.TopoDS.topods.Face(faces_explorer.Current()) + newface = self.write_face(l_shells[0][iface][0], + l_shells[0][iface][1], topoface, tol) + + # add face to compound + compound_builder.Add(comp, newface) + iface += 1 + faces_explorer.Next() + + new_shell = self.combine_faces(comp, 0.01) + itype = OCC.TopoDS.TopoDS_Shape.ShapeType(new_shell) + # add the new shell to the global compound + global_compound_builder.Add(global_comp, new_shell) + + print("Shell {0} of type {1} Processed ".format(0, itype)) + print "==============================================" + + self.write_shape_to_file(global_comp, self.outfile) + + def write_shape_to_file(self, shape, filename): + """ + Abstract method to write the 'shape' to the `filename`. + + Not implemented, it has to be implemented in subclasses. + """ + raise NotImplementedError(\ + "Subclass must implement abstract method " +\ + self.__class__.__name__ + ".write_shape_to_file") + + def plot(self, plot_file=None, save_fig=False): + """ + Method to plot a file. If `plot_file` is not given it plots `self.shape`. + + :param string plot_file: the filename you want to plot. + :param bool save_fig: a flag to save the figure in png or not. If True the + plot is not shown. + + :return: figure: matlplotlib structure for the figure of the chosen geometry + :rtype: matplotlib.pyplot.figure + """ + if plot_file is None: + shape = self.shape + plot_file = self.infile + else: + shape = self.load_shape_from_file(plot_file) + + stl_writer = StlAPI_Writer() + # Do not switch SetASCIIMode() from False to True. + stl_writer.SetASCIIMode(False) + stl_writer.Write(shape, 'aux_figure.stl') + + # Create a new plot + figure = pyplot.figure() + axes = mplot3d.Axes3D(figure) + + # Load the STL files and add the vectors to the plot + stl_mesh = mesh.Mesh.from_file('aux_figure.stl') + os.remove('aux_figure.stl') + axes.add_collection3d( + mplot3d.art3d.Poly3DCollection(stl_mesh.vectors / 1000)) + + # Get the limits of the axis and center the geometry + max_dim = np.array([\ + np.max(stl_mesh.vectors[:, :, 0]) / 1000,\ + np.max(stl_mesh.vectors[:, :, 1]) / 1000,\ + np.max(stl_mesh.vectors[:, :, 2]) / 1000]) + min_dim = np.array([\ + np.min(stl_mesh.vectors[:, :, 0]) / 1000,\ + np.min(stl_mesh.vectors[:, :, 1]) / 1000,\ + np.min(stl_mesh.vectors[:, :, 2]) / 1000]) + + max_lenght = np.max(max_dim - min_dim) + axes.set_xlim(\ + -.6 * max_lenght + (max_dim[0] + min_dim[0]) / 2,\ + .6 * max_lenght + (max_dim[0] + min_dim[0]) / 2) + axes.set_ylim(\ + -.6 * max_lenght + (max_dim[1] + min_dim[1]) / 2,\ + .6 * max_lenght + (max_dim[1] + min_dim[1]) / 2) + axes.set_zlim(\ + -.6 * max_lenght + (max_dim[2] + min_dim[2]) / 2,\ + .6 * max_lenght + (max_dim[2] + min_dim[2]) / 2) + + # Show the plot to the screen + if not save_fig: + pyplot.show() + else: + figure.savefig(plot_file.split('.')[0] + '.png') + + return figure + + def show(self, show_file=None): + """ + Method to show a file. If `show_file` is not given it plots `self.shape`. + + :param string show_file: the filename you want to show. + """ + if show_file is None: + shape = self.shape + else: + shape = self.load_shape_from_file(show_file) + + display, start_display, __, __ = init_display() + display.FitAll() + display.DisplayShape(shape, update=True) + + # Show the plot to the screen + start_display() diff --git a/pygem/openfhandler.py b/pygem/openfhandler.py index e47987f..09ed436 100644 --- a/pygem/openfhandler.py +++ b/pygem/openfhandler.py @@ -6,85 +6,85 @@ class OpenFoamHandler(fh.FileHandler): - """ - OpenFOAM mesh file handler class. - - :cvar string infile: name of the input file to be processed. - :cvar string outfile: name of the output file where to write in. - :cvar list extensions: extensions of the input/output files. It is equal to [''] since - openFOAM files do not have extension. - """ - - def __init__(self): - super(OpenFoamHandler, self).__init__() - self.extensions = [''] - - def parse(self, filename): - """ - Method to parse the `filename`. It returns a matrix with all the coordinates. - - :param string filename: name of the input file. - - :return: mesh_points: it is a `n_points`-by-3 matrix containing the coordinates of - the points of the mesh - :rtype: numpy.ndarray - - .. todo:: - - - specify when it works - """ - self._check_filename_type(filename) - self._check_extension(filename) - - self.infile = filename - - nrow = 0 - i = 0 - with open(self.infile, 'r') as input_file: - for line in input_file: - nrow += 1 - if nrow == 19: - n_points = int(line) - mesh_points = np.zeros(shape=(n_points, 3)) - if 20 < nrow < 21 + n_points: - line = line[line.index("(") + 1:line.rindex(")")] - j = 0 - for number in line.split(): - mesh_points[i][j] = float(number) - j += 1 - i += 1 - - return mesh_points - - def write(self, mesh_points, filename): - """ - Writes a openFOAM file, called filename, copying all the lines from self.filename but - the coordinates. mesh_points is a matrix that contains the new coordinates to - write in the openFOAM file. - - :param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix containing - the coordinates of the points of the mesh. - :param string filename: name of the output file. - - .. todo:: DOCS - """ - self._check_filename_type(filename) - self._check_extension(filename) - self._check_infile_instantiation() - - self.outfile = filename - - n_points = mesh_points.shape[0] - nrow = 0 - i = 0 - with open(self.infile, - 'r') as input_file, open(self.outfile, 'w') as output_file: - for line in input_file: - nrow += 1 - if 20 < nrow < 21 + n_points: - output_file.write('(' + str(mesh_points[i][0]) + ' ' + str(mesh_points[i][1]) + \ - ' ' + str(mesh_points[i][2]) +')') - output_file.write('\n') - i += 1 - else: - output_file.write(line) + """ + OpenFOAM mesh file handler class. + + :cvar string infile: name of the input file to be processed. + :cvar string outfile: name of the output file where to write in. + :cvar list extensions: extensions of the input/output files. It is equal to [''] since + openFOAM files do not have extension. + """ + + def __init__(self): + super(OpenFoamHandler, self).__init__() + self.extensions = [''] + + def parse(self, filename): + """ + Method to parse the `filename`. It returns a matrix with all the coordinates. + + :param string filename: name of the input file. + + :return: mesh_points: it is a `n_points`-by-3 matrix containing the coordinates of + the points of the mesh + :rtype: numpy.ndarray + + .. todo:: + + - specify when it works + """ + self._check_filename_type(filename) + self._check_extension(filename) + + self.infile = filename + + nrow = 0 + i = 0 + with open(self.infile, 'r') as input_file: + for line in input_file: + nrow += 1 + if nrow == 19: + n_points = int(line) + mesh_points = np.zeros(shape=(n_points, 3)) + if 20 < nrow < 21 + n_points: + line = line[line.index("(") + 1:line.rindex(")")] + j = 0 + for number in line.split(): + mesh_points[i][j] = float(number) + j += 1 + i += 1 + + return mesh_points + + def write(self, mesh_points, filename): + """ + Writes a openFOAM file, called filename, copying all the lines from self.filename but + the coordinates. mesh_points is a matrix that contains the new coordinates to + write in the openFOAM file. + + :param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix containing + the coordinates of the points of the mesh. + :param string filename: name of the output file. + + .. todo:: DOCS + """ + self._check_filename_type(filename) + self._check_extension(filename) + self._check_infile_instantiation() + + self.outfile = filename + + n_points = mesh_points.shape[0] + nrow = 0 + i = 0 + with open(self.infile, 'r') as input_file, open(self.outfile, + 'w') as output_file: + for line in input_file: + nrow += 1 + if 20 < nrow < 21 + n_points: + output_file.write('(' + str(mesh_points[i][0]) + ' ' + str(mesh_points[i][1]) + \ + ' ' + str(mesh_points[i][2]) +')') + output_file.write('\n') + i += 1 + else: + output_file.write(line) diff --git a/pygem/params.py b/pygem/params.py index 73fe642..d4c718d 100644 --- a/pygem/params.py +++ b/pygem/params.py @@ -18,556 +18,580 @@ class FFDParameters(object): - """ - Class that handles the Free Form Deformation parameters in terms of FFD - bounding box and weight of the FFD control points. - - :param list n_control_points: number of control points in the x, y, and z - direction. If not provided it is set to [2, 2, 2]. - - :cvar float length_box_x: length of the FFD bounding box in the x direction - (local coordinate system). - :cvar float length_box_y: length of the FFD bounding box in the y direction - (local coordinate system). - :cvar float length_box_z: length of the FFD bounding box in the z direction - (local coordinate system). - - :cvar numpy.ndarray origin_box: a 3-by-1 vector of float numbers - representing the x, y and z coordinates of the origin of the FFD - bounding box. - - :cvar float rot_angle_x: rotation angle around x axis of the FFD bounding - box. - :cvar float rot_angle_y: rotation angle around y axis of the FFD bounding - box. - :cvar float rot_angle_z: rotation angle around z axis of the FFD bounding - box. - - :cvar list n_control_points: list of 3 int representing the number of - control points in the x, y, and z direction. - - :cvar numpy.ndarray array_mu_x: collects the displacements (weights) along - x, normalized with the box lenght x. - :cvar numpy.ndarray array_mu_y: collects the displacements (weights) along - y, normalized with the box lenght y. - :cvar numpy.ndarray array_mu_z: collects the displacements (weights) along - z, normalized with the box lenght z. - - :cvar numpy.ndarray psi_mapping: map from the pysical domain to the - reference domain. - :cvar numpy.ndarray inv_psi_mapping: map from the reference domain to the - physical domain. - - :cvar numpy.ndarray rotation_matrix: rotation matrix (according to - rot_angle_x, rot_angle_y, rot_angle_z). - - :cvar numpy.ndarray position_vertex_0: position of the first vertex of the - FFD bounding box. It is always equal to the member `origin_box`. - :cvar numpy.ndarray position_vertex_1: position of the second vertex of the - FFD bounding box. - :cvar numpy.ndarray position_vertex_2: position of the third vertex of the - FFD bounding box. - :cvar numpy.ndarray position_vertex_3: position of the fourth vertex of the - FFD bounding box. - - :Example: from file - - >>> import pygem.params as ffdp - - >>> # Reading an existing file - >>> params1 = ffdp.FFDParameters() - >>> params1.read_parameters(filename='tests/test_datasets/parameters_test_ffd_identity.prm') - - >>> # Creating a default parameters file with the right dimensions (if the file does not exists - >>> # it is created with that name). So it is possible to manually edit it and read it again. - >>> params2 = ffdp.FFDParameters(n_control_points=[2, 3, 2]) - >>> params2.read_parameters(filename='parameters_test.prm') - - >>> # Creating bounding box of the given shape - >>> from OCC.IGESControl import IGESControl_Reader - >>> params3 = ffdp.FFDParameters() - >>> reader = IGESControl_Reader() - >>> reader.ReadFile('tests/test_datasets/test_pipe.igs') - >>> reader.TransferRoots() - >>> shape = reader.Shape() - >>> params3.build_bounding_box(shape) - - .. note:: - Four vertex (non coplanar) are sufficient to uniquely identify a - parallelepiped. - If the four vertex are coplanar, an assert is thrown when - affine_points_fit is used. - """ - def __init__(self, n_control_points=None): - self.conversion_unit = 1. - - self.lenght_box = np.array([1., 1., 1.]) - self.origin_box = np.array([0., 0., 0.]) - self.rot_angle = np.array([0., 0., 0.]) + Class that handles the Free Form Deformation parameters in terms of FFD + bounding box and weight of the FFD control points. + + :param list n_control_points: number of control points in the x, y, and z + direction. If not provided it is set to [2, 2, 2]. + + :cvar float length_box_x: length of the FFD bounding box in the x direction + (local coordinate system). + :cvar float length_box_y: length of the FFD bounding box in the y direction + (local coordinate system). + :cvar float length_box_z: length of the FFD bounding box in the z direction + (local coordinate system). + + :cvar numpy.ndarray origin_box: a 3-by-1 vector of float numbers + representing the x, y and z coordinates of the origin of the FFD + bounding box. + + :cvar float rot_angle_x: rotation angle around x axis of the FFD bounding + box. + :cvar float rot_angle_y: rotation angle around y axis of the FFD bounding + box. + :cvar float rot_angle_z: rotation angle around z axis of the FFD bounding + box. + + :cvar list n_control_points: list of 3 int representing the number of + control points in the x, y, and z direction. + + :cvar numpy.ndarray array_mu_x: collects the displacements (weights) along + x, normalized with the box lenght x. + :cvar numpy.ndarray array_mu_y: collects the displacements (weights) along + y, normalized with the box lenght y. + :cvar numpy.ndarray array_mu_z: collects the displacements (weights) along + z, normalized with the box lenght z. + + :cvar numpy.ndarray psi_mapping: map from the pysical domain to the + reference domain. + :cvar numpy.ndarray inv_psi_mapping: map from the reference domain to the + physical domain. + + :cvar numpy.ndarray rotation_matrix: rotation matrix (according to + rot_angle_x, rot_angle_y, rot_angle_z). + + :cvar numpy.ndarray position_vertex_0: position of the first vertex of the + FFD bounding box. It is always equal to the member `origin_box`. + :cvar numpy.ndarray position_vertex_1: position of the second vertex of the + FFD bounding box. + :cvar numpy.ndarray position_vertex_2: position of the third vertex of the + FFD bounding box. + :cvar numpy.ndarray position_vertex_3: position of the fourth vertex of the + FFD bounding box. + + :Example: from file + + >>> import pygem.params as ffdp + + >>> # Reading an existing file + >>> params1 = ffdp.FFDParameters() + >>> params1.read_parameters(filename='tests/test_datasets/parameters_test_ffd_identity.prm') + + >>> # Creating a default parameters file with the right dimensions (if the file does not exists + >>> # it is created with that name). So it is possible to manually edit it and read it again. + >>> params2 = ffdp.FFDParameters(n_control_points=[2, 3, 2]) + >>> params2.read_parameters(filename='parameters_test.prm') + + >>> # Creating bounding box of the given shape + >>> from OCC.IGESControl import IGESControl_Reader + >>> params3 = ffdp.FFDParameters() + >>> reader = IGESControl_Reader() + >>> reader.ReadFile('tests/test_datasets/test_pipe.igs') + >>> reader.TransferRoots() + >>> shape = reader.Shape() + >>> params3.build_bounding_box(shape) + + .. note:: + Four vertex (non coplanar) are sufficient to uniquely identify a + parallelepiped. + If the four vertex are coplanar, an assert is thrown when + affine_points_fit is used. - if n_control_points is None: - n_control_points = [2, 2, 2] - self.n_control_points = np.array(n_control_points) + """ - - self.array_mu_x = np.zeros(self.n_control_points) - self.array_mu_y = np.zeros(self.n_control_points) - self.array_mu_z = np.zeros(self.n_control_points) - - self.psi_mapping = np.diag(1. / self.lenght_box) - self.inv_psi_mapping = np.diag(self.lenght_box) - - self.rotation_matrix = np.eye(3) - self.position_vertex_0 = self.origin_box - self.position_vertex_1 = np.array([1., 0., 0.]) - self.position_vertex_2 = np.array([0., 1., 0.]) - self.position_vertex_3 = np.array([0., 0., 1.]) - - - def read_parameters(self, filename='parameters.prm'): - """ - Reads in the parameters file and fill the self structure. - - :param string filename: parameters file to be read in. - """ - if not isinstance(filename, str): - raise TypeError("filename must be a string") - - # Checks if the parameters file exists. If not it writes the default - # class into filename. - if not os.path.isfile(filename): - self.write_parameters(filename) - return - - config = configparser.RawConfigParser() - config.read(filename) - - self.n_control_points[0] = config.getint('Box info', 'n control points x') - self.n_control_points[1] = config.getint('Box info', 'n control points y') - self.n_control_points[2] = config.getint('Box info', 'n control points z') - - self.lenght_box[0] = config.getfloat('Box info', 'box lenght x') - self.lenght_box[1] = config.getfloat('Box info', 'box lenght y') - self.lenght_box[2] = config.getfloat('Box info', 'box lenght z') - - self.origin_box[0] = config.getfloat('Box info', 'box origin x') - self.origin_box[1] = config.getfloat('Box info', 'box origin y') - self.origin_box[2] = config.getfloat('Box info', 'box origin z') - - self.rot_angle[0] = config.getfloat('Box info', 'rotation angle x') - self.rot_angle[1] = config.getfloat('Box info', 'rotation angle y') - self.rot_angle[2] = config.getfloat('Box info', 'rotation angle z') - - self.array_mu_x = np.zeros(self.n_control_points) - self.array_mu_y = np.zeros(self.n_control_points) - self.array_mu_z = np.zeros(self.n_control_points) - - mux = config.get('Parameters weights', 'parameter x') - muy = config.get('Parameters weights', 'parameter y') - muz = config.get('Parameters weights', 'parameter z') - - for line in mux.split('\n'): - values = np.array(line.split()) - self.array_mu_x[tuple(map(int, values[0:3]))] = float(values[3]) - - for line in muy.split('\n'): - values = line.split() - self.array_mu_y[tuple(map(int, values[0:3]))] = float(values[3]) - - for line in muz.split('\n'): - values = line.split() - self.array_mu_z[tuple(map(int, values[0:3]))] = float(values[3]) - - self.rotation_matrix = at.angles2matrix( - radians(self.rot_angle[2]), - radians(self.rot_angle[1]), - radians(self.rot_angle[0]) - ) - - self.position_vertex_0 = self.origin_box - self.position_vertex_1 = self.position_vertex_0 + \ - np.dot(self.rotation_matrix, [self.lenght_box[0], 0, 0]) - self.position_vertex_2 = self.position_vertex_0 + \ - np.dot(self.rotation_matrix, [0, self.lenght_box[1], 0]) - self.position_vertex_3 = self.position_vertex_0 + \ - np.dot(self.rotation_matrix, [0, 0, self.lenght_box[2]]) - - self.psi_mapping = np.diag(1. / self.lenght_box) - self.inv_psi_mapping = np.diag(self.lenght_box) - - - def write_parameters(self, filename='parameters.prm'): - """ - This method writes a parameters file (.prm) called `filename` and fills - it with all the parameters class members. - - :param string filename: parameters file to be written out. - """ - if not isinstance(filename, str): - raise TypeError("filename must be a string") - - output_string = "" - output_string += '\n[Box info]\n' - output_string += '# This section collects all the properties of the' - output_string += ' FFD bounding box.\n' - - output_string += '\n# n control points indicates the number of control' - output_string += ' points in each direction (x, y, z).\n' - output_string += '# For example, to create a 2 x 3 x 2 grid, use the' - output_string += ' following: n control points: 2, 3, 2\n' - output_string += 'n control points x: ' + str(self.n_control_points[0]) + '\n' - output_string += 'n control points y: ' + str(self.n_control_points[1]) + '\n' - output_string += 'n control points z: ' + str(self.n_control_points[2]) + '\n' - - output_string += '\n# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).\n' - output_string += '# It uses the local coordinate system.\n' - output_string += '# For example to create a 2 x 1.5 x 3 meters box use the following: lenght box: 2.0, 1.5, 3.0\n' - output_string += 'box lenght x: ' + str(self.lenght_box[0]) + '\n' - output_string += 'box lenght y: ' + str(self.lenght_box[1]) + '\n' - output_string += 'box lenght z: ' + str(self.lenght_box[2]) + '\n' - - output_string += '\n# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of\n' - output_string += '# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].\n' - output_string += '# See section "Parameters weights" for more details.\n' - output_string += '# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.\n' - output_string += 'box origin x: ' + str(self.origin_box[0]) + '\n' - output_string += 'box origin y: ' + str(self.origin_box[1]) + '\n' - output_string += 'box origin z: ' + str(self.origin_box[2]) + '\n' - - output_string += '\n# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.\n' - output_string += '# The rotation is done with respect to the box origin.\n' - output_string += '# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.\n' - output_string += 'rotation angle x: ' + str(self.rot_angle[0]) + '\n' - output_string += 'rotation angle y: ' + str(self.rot_angle[1]) + '\n' - output_string += 'rotation angle z: ' + str(self.rot_angle[2]) + '\n' - - output_string += '\n\n[Parameters weights]\n' - output_string += '# This section describes the weights of the FFD control points.\n' - output_string += '# We adopt the following convention:\n' - output_string += '# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.\n' - output_string += '# If a weight is equal to zero you can discard the line since the default is zero.\n' - output_string += '#\n' - output_string += '# | x index | y index | z index | weight |\n' - output_string += '# --------------------------------------\n' - output_string += '# | 0 | 0 | 0 | 1.0 |\n' - output_string += '# | 0 | 1 | 1 | 0.0 | --> you can erase this line without effects\n' - output_string += '# | 0 | 1 | 0 | -2.1 |\n' - output_string += '# | 0 | 0 | 1 | 3.4 |\n' - - output_string += '\n# parameter x collects the displacements along x, normalized with the box lenght x.' - output_string += '\nparameter x:' - offset = 1 - for i in range(0, self.n_control_points[0]): - for j in range(0, self.n_control_points[1]): - for k in range(0, self.n_control_points[2]): - output_string += offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_x[i][j][k]) + '\n' - offset = 13 - - output_string += '\n# parameter y collects the displacements along y, normalized with the box lenght y.' - output_string += '\nparameter y:' - offset = 1 - for i in range(0, self.n_control_points[0]): - for j in range(0, self.n_control_points[1]): - for k in range(0, self.n_control_points[2]): - output_string += offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_y[i][j][k]) + '\n' - offset = 13 - - output_string += '\n# parameter z collects the displacements along z, normalized with the box lenght z.' - output_string += '\nparameter z:' - offset = 1 - for i in range(0, self.n_control_points[0]): - for j in range(0, self.n_control_points[1]): - for k in range(0, self.n_control_points[2]): - output_string += offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_z[i][j][k]) + '\n' - offset = 13 - - with open(filename, 'w') as f: - f.write(output_string) - - - def __str__(self): - """ - This method prints all the FFD parameters on the screen. Its purpose is - for debugging. - """ - string = "" - string += 'conversion_unit = {}\n'.format(self.conversion_unit) - string += 'n_control_points = {}\n\n'.format(self.n_control_points) - string += 'lenght_box = {}\n'.format(self.lenght_box) - string += 'origin_box = {}\n'.format(self.origin_box) - string += 'rot_angle = {}\n'.format(self.rot_angle) - string += '\narray_mu_x =\n{}\n'.format(self.array_mu_x) - string += '\narray_mu_y =\n{}\n'.format(self.array_mu_y) - string += '\narray_mu_z =\n{}\n'.format(self.array_mu_z) - string += '\npsi_mapping = \n{}\n'.format(self.psi_mapping) - string += '\nrotation_matrix = \n{}\n'.format(self.rotation_matrix) - string += '\nposition_vertex_0 = {}\n'.format(self.position_vertex_0) - string += 'position_vertex_1 = {}\n'.format(self.position_vertex_1) - string += 'position_vertex_2 = {}\n'.format(self.position_vertex_2) - string += 'position_vertex_3 = {}\n'.format(self.position_vertex_3) - return string - - def build_bounding_box(self, shape, tol=1e-6, triangulate=False, triangulate_tol=1e-1): - """ - Builds a bounding box around the given shape. ALl parameters (with the - exception of array_mu_x/y/z) are set to match the computed box. - - :param TopoDS_Shape shape: or a subclass such as TopoDS_Face the shape - to compute the bounding box from - :param float tol: tolerance of the computed bounding box - :param bool triangulate: Should shape be triangulated before the - boudning box is created. - - If ``True`` only the dimensions of the bb will take into account - every part of the shape (also not *visible*) - - If ``False`` only the *visible* part is taken into account - - *Explanation:* every UV-Surface has to be rectangular. When a solid - is created surfaces are trimmed. the trimmed part, however, is - still saved inside a file. It is just *invisible* when drawn in a - program - - :param float triangulate_tol: tolerance of triangulation (size of - created triangles) - """ - min_xyz, max_xyz = self._calculate_bb_dimension(shape, tol, triangulate, triangulate_tol) - self.origin_box = min_xyz - self._set_box_dimensions(min_xyz, max_xyz) - self._set_position_of_vertices() - self._set_mapping() - self._set_transformation_params_to_zero() - - def _set_box_dimensions(self, min_xyz, max_xyz): - """ - Dimensions of the cage are set as distance from the origin (minimum) of - the cage to the maximal point in each dimension. - - :param iterable min_xyz: three values representing the minimal values of - the bounding box in XYZ respectively - :param iterable max_xyz: three values representing the maximal values of - the bounding box in XYZ respectively - """ - dims = [max_xyz[i] - min_xyz[i] for i in range(3)] - self.lenght_box = np.asarray(dims) - - def _set_position_of_vertices(self): - """ - Vertices of the control box around the object are set in this method. - Four vertices (non coplanar) are sufficient to uniquely identify a - parallelepiped -- the second half of the box is created as a mirror - reflection of the first four vertices. - """ - self.position_vertex_0 = self.origin_box - self.position_vertex_1 = self.origin_box + np.array([self.lenght_box[0], .0, .0]) - self.position_vertex_2 = self.origin_box + np.array([.0, self.lenght_box[1], .0]) - self.position_vertex_3 = self.origin_box + np.array([.0, .0, self.lenght_box[2]]) - - def _set_mapping(self): - """ - This method sets mapping from physcial domain to the reference domain - (``psi_mapping``) as well as inverse mapping (``inv_psi_mapping``). - """ - self.psi_mapping = np.diag([1. / self.lenght_box[i] for i in range(3)]) - self.inv_psi_mapping = np.diag(self.lenght_box) - - def _set_transformation_params_to_zero(self): - """ - Sets transfomration parameters (``array_mu_x, array_mu_y, array_mu_z``) - to arrays of zeros (``numpy.zeros``). The shape of arrays corresponds to - the number of control points in each dimension. - """ - ctrl_pnts = self.n_control_points - self.array_mu_x = np.zeros(ctrl_pnts) - self.array_mu_y = np.zeros(ctrl_pnts) - self.array_mu_z = np.zeros(ctrl_pnts) - - @staticmethod - def _calculate_bb_dimension(shape, tol=1e-6, triangulate=False, triangulate_tol=1e-1): - """ Calculate dimensions (minima and maxima) of a box bounding the - - :param TopoDS_Shape shape: or a subclass such as TopoDS_Face the shape - to compute the bounding box from - :param float tol: tolerance of the computed bounding box - :param bool triangulate: Should shape be triangulated before the - boudning box is created. - - If ``True`` only the dimensions of the bb will take into account - every part of the shape (also not *visible*) - - If ``False`` only the *visible* part is taken into account - - \*See :meth:`~params.FFDParameters.build_bounding_box` - :param float triangulate_tol: tolerance of triangulation (size of - created triangles) - :return: coordinates of minima and maxima along XYZ - :rtype: tuple - """ - bbox = Bnd_Box() - bbox.SetGap(tol) - if triangulate: - BRepMesh_IncrementalMesh(shape, triangulate_tol) - brepbndlib_Add(shape, bbox, triangulate) - xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get() - xyz_min = np.array([xmin, ymin, zmin]) - xyz_max = np.array([xmax, ymax, zmax]) - return xyz_min, xyz_max + def __init__(self, n_control_points=None): + self.conversion_unit = 1. + + self.lenght_box = np.array([1., 1., 1.]) + self.origin_box = np.array([0., 0., 0.]) + self.rot_angle = np.array([0., 0., 0.]) + + if n_control_points is None: + n_control_points = [2, 2, 2] + self.n_control_points = np.array(n_control_points) + + self.array_mu_x = np.zeros(self.n_control_points) + self.array_mu_y = np.zeros(self.n_control_points) + self.array_mu_z = np.zeros(self.n_control_points) + + self.psi_mapping = np.diag(1. / self.lenght_box) + self.inv_psi_mapping = np.diag(self.lenght_box) + + self.rotation_matrix = np.eye(3) + self.position_vertex_0 = self.origin_box + self.position_vertex_1 = np.array([1., 0., 0.]) + self.position_vertex_2 = np.array([0., 1., 0.]) + self.position_vertex_3 = np.array([0., 0., 1.]) + + def read_parameters(self, filename='parameters.prm'): + """ + Reads in the parameters file and fill the self structure. + + :param string filename: parameters file to be read in. + """ + if not isinstance(filename, str): + raise TypeError("filename must be a string") + + # Checks if the parameters file exists. If not it writes the default + # class into filename. + if not os.path.isfile(filename): + self.write_parameters(filename) + return + + config = configparser.RawConfigParser() + config.read(filename) + + self.n_control_points[0] = config.getint('Box info', + 'n control points x') + self.n_control_points[1] = config.getint('Box info', + 'n control points y') + self.n_control_points[2] = config.getint('Box info', + 'n control points z') + + self.lenght_box[0] = config.getfloat('Box info', 'box lenght x') + self.lenght_box[1] = config.getfloat('Box info', 'box lenght y') + self.lenght_box[2] = config.getfloat('Box info', 'box lenght z') + + self.origin_box[0] = config.getfloat('Box info', 'box origin x') + self.origin_box[1] = config.getfloat('Box info', 'box origin y') + self.origin_box[2] = config.getfloat('Box info', 'box origin z') + + self.rot_angle[0] = config.getfloat('Box info', 'rotation angle x') + self.rot_angle[1] = config.getfloat('Box info', 'rotation angle y') + self.rot_angle[2] = config.getfloat('Box info', 'rotation angle z') + + self.array_mu_x = np.zeros(self.n_control_points) + self.array_mu_y = np.zeros(self.n_control_points) + self.array_mu_z = np.zeros(self.n_control_points) + + mux = config.get('Parameters weights', 'parameter x') + muy = config.get('Parameters weights', 'parameter y') + muz = config.get('Parameters weights', 'parameter z') + + for line in mux.split('\n'): + values = np.array(line.split()) + self.array_mu_x[tuple(map(int, values[0:3]))] = float(values[3]) + + for line in muy.split('\n'): + values = line.split() + self.array_mu_y[tuple(map(int, values[0:3]))] = float(values[3]) + + for line in muz.split('\n'): + values = line.split() + self.array_mu_z[tuple(map(int, values[0:3]))] = float(values[3]) + + self.rotation_matrix = at.angles2matrix( + radians(self.rot_angle[2]), radians(self.rot_angle[1]), + radians(self.rot_angle[0])) + + self.position_vertex_0 = self.origin_box + self.position_vertex_1 = self.position_vertex_0 + \ + np.dot(self.rotation_matrix, [self.lenght_box[0], 0, 0]) + self.position_vertex_2 = self.position_vertex_0 + \ + np.dot(self.rotation_matrix, [0, self.lenght_box[1], 0]) + self.position_vertex_3 = self.position_vertex_0 + \ + np.dot(self.rotation_matrix, [0, 0, self.lenght_box[2]]) + + self.psi_mapping = np.diag(1. / self.lenght_box) + self.inv_psi_mapping = np.diag(self.lenght_box) + + def write_parameters(self, filename='parameters.prm'): + """ + This method writes a parameters file (.prm) called `filename` and fills + it with all the parameters class members. + + :param string filename: parameters file to be written out. + """ + if not isinstance(filename, str): + raise TypeError("filename must be a string") + + output_string = "" + output_string += '\n[Box info]\n' + output_string += '# This section collects all the properties of the' + output_string += ' FFD bounding box.\n' + + output_string += '\n# n control points indicates the number of control' + output_string += ' points in each direction (x, y, z).\n' + output_string += '# For example, to create a 2 x 3 x 2 grid, use the' + output_string += ' following: n control points: 2, 3, 2\n' + output_string += 'n control points x: ' + str( + self.n_control_points[0]) + '\n' + output_string += 'n control points y: ' + str( + self.n_control_points[1]) + '\n' + output_string += 'n control points z: ' + str( + self.n_control_points[2]) + '\n' + + output_string += '\n# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).\n' + output_string += '# It uses the local coordinate system.\n' + output_string += '# For example to create a 2 x 1.5 x 3 meters box use the following: lenght box: 2.0, 1.5, 3.0\n' + output_string += 'box lenght x: ' + str(self.lenght_box[0]) + '\n' + output_string += 'box lenght y: ' + str(self.lenght_box[1]) + '\n' + output_string += 'box lenght z: ' + str(self.lenght_box[2]) + '\n' + + output_string += '\n# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of\n' + output_string += '# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].\n' + output_string += '# See section "Parameters weights" for more details.\n' + output_string += '# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.\n' + output_string += 'box origin x: ' + str(self.origin_box[0]) + '\n' + output_string += 'box origin y: ' + str(self.origin_box[1]) + '\n' + output_string += 'box origin z: ' + str(self.origin_box[2]) + '\n' + + output_string += '\n# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.\n' + output_string += '# The rotation is done with respect to the box origin.\n' + output_string += '# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.\n' + output_string += 'rotation angle x: ' + str(self.rot_angle[0]) + '\n' + output_string += 'rotation angle y: ' + str(self.rot_angle[1]) + '\n' + output_string += 'rotation angle z: ' + str(self.rot_angle[2]) + '\n' + + output_string += '\n\n[Parameters weights]\n' + output_string += '# This section describes the weights of the FFD control points.\n' + output_string += '# We adopt the following convention:\n' + output_string += '# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.\n' + output_string += '# If a weight is equal to zero you can discard the line since the default is zero.\n' + output_string += '#\n' + output_string += '# | x index | y index | z index | weight |\n' + output_string += '# --------------------------------------\n' + output_string += '# | 0 | 0 | 0 | 1.0 |\n' + output_string += '# | 0 | 1 | 1 | 0.0 | --> you can erase this line without effects\n' + output_string += '# | 0 | 1 | 0 | -2.1 |\n' + output_string += '# | 0 | 0 | 1 | 3.4 |\n' + + output_string += '\n# parameter x collects the displacements along x, normalized with the box lenght x.' + output_string += '\nparameter x:' + offset = 1 + for i in range(0, self.n_control_points[0]): + for j in range(0, self.n_control_points[1]): + for k in range(0, self.n_control_points[2]): + output_string += offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ + ' ' + str(self.array_mu_x[i][j][k]) + '\n' + offset = 13 + + output_string += '\n# parameter y collects the displacements along y, normalized with the box lenght y.' + output_string += '\nparameter y:' + offset = 1 + for i in range(0, self.n_control_points[0]): + for j in range(0, self.n_control_points[1]): + for k in range(0, self.n_control_points[2]): + output_string += offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ + ' ' + str(self.array_mu_y[i][j][k]) + '\n' + offset = 13 + + output_string += '\n# parameter z collects the displacements along z, normalized with the box lenght z.' + output_string += '\nparameter z:' + offset = 1 + for i in range(0, self.n_control_points[0]): + for j in range(0, self.n_control_points[1]): + for k in range(0, self.n_control_points[2]): + output_string += offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ + ' ' + str(self.array_mu_z[i][j][k]) + '\n' + offset = 13 + + with open(filename, 'w') as f: + f.write(output_string) + + def __str__(self): + """ + This method prints all the FFD parameters on the screen. Its purpose is + for debugging. + """ + string = "" + string += 'conversion_unit = {}\n'.format(self.conversion_unit) + string += 'n_control_points = {}\n\n'.format(self.n_control_points) + string += 'lenght_box = {}\n'.format(self.lenght_box) + string += 'origin_box = {}\n'.format(self.origin_box) + string += 'rot_angle = {}\n'.format(self.rot_angle) + string += '\narray_mu_x =\n{}\n'.format(self.array_mu_x) + string += '\narray_mu_y =\n{}\n'.format(self.array_mu_y) + string += '\narray_mu_z =\n{}\n'.format(self.array_mu_z) + string += '\npsi_mapping = \n{}\n'.format(self.psi_mapping) + string += '\nrotation_matrix = \n{}\n'.format(self.rotation_matrix) + string += '\nposition_vertex_0 = {}\n'.format(self.position_vertex_0) + string += 'position_vertex_1 = {}\n'.format(self.position_vertex_1) + string += 'position_vertex_2 = {}\n'.format(self.position_vertex_2) + string += 'position_vertex_3 = {}\n'.format(self.position_vertex_3) + return string + + def build_bounding_box(self, + shape, + tol=1e-6, + triangulate=False, + triangulate_tol=1e-1): + """ + Builds a bounding box around the given shape. ALl parameters (with the + exception of array_mu_x/y/z) are set to match the computed box. + + :param TopoDS_Shape shape: or a subclass such as TopoDS_Face the shape + to compute the bounding box from + :param float tol: tolerance of the computed bounding box + :param bool triangulate: Should shape be triangulated before the + boudning box is created. + + If ``True`` only the dimensions of the bb will take into account + every part of the shape (also not *visible*) + + If ``False`` only the *visible* part is taken into account + + *Explanation:* every UV-Surface has to be rectangular. When a solid + is created surfaces are trimmed. the trimmed part, however, is + still saved inside a file. It is just *invisible* when drawn in a + program + + :param float triangulate_tol: tolerance of triangulation (size of + created triangles) + """ + min_xyz, max_xyz = self._calculate_bb_dimension(shape, tol, triangulate, + triangulate_tol) + self.origin_box = min_xyz + self._set_box_dimensions(min_xyz, max_xyz) + self._set_position_of_vertices() + self._set_mapping() + self._set_transformation_params_to_zero() + + def _set_box_dimensions(self, min_xyz, max_xyz): + """ + Dimensions of the cage are set as distance from the origin (minimum) of + the cage to the maximal point in each dimension. + + :param iterable min_xyz: three values representing the minimal values of + the bounding box in XYZ respectively + :param iterable max_xyz: three values representing the maximal values of + the bounding box in XYZ respectively + """ + dims = [max_xyz[i] - min_xyz[i] for i in range(3)] + self.lenght_box = np.asarray(dims) + + def _set_position_of_vertices(self): + """ + Vertices of the control box around the object are set in this method. + Four vertices (non coplanar) are sufficient to uniquely identify a + parallelepiped -- the second half of the box is created as a mirror + reflection of the first four vertices. + """ + self.position_vertex_0 = self.origin_box + self.position_vertex_1 = self.origin_box + np.array( + [self.lenght_box[0], .0, .0]) + self.position_vertex_2 = self.origin_box + np.array( + [.0, self.lenght_box[1], .0]) + self.position_vertex_3 = self.origin_box + np.array( + [.0, .0, self.lenght_box[2]]) + + def _set_mapping(self): + """ + This method sets mapping from physcial domain to the reference domain + (``psi_mapping``) as well as inverse mapping (``inv_psi_mapping``). + """ + self.psi_mapping = np.diag([1. / self.lenght_box[i] for i in range(3)]) + self.inv_psi_mapping = np.diag(self.lenght_box) + + def _set_transformation_params_to_zero(self): + """ + Sets transfomration parameters (``array_mu_x, array_mu_y, array_mu_z``) + to arrays of zeros (``numpy.zeros``). The shape of arrays corresponds to + the number of control points in each dimension. + """ + ctrl_pnts = self.n_control_points + self.array_mu_x = np.zeros(ctrl_pnts) + self.array_mu_y = np.zeros(ctrl_pnts) + self.array_mu_z = np.zeros(ctrl_pnts) + + @staticmethod + def _calculate_bb_dimension(shape, + tol=1e-6, + triangulate=False, + triangulate_tol=1e-1): + """ Calculate dimensions (minima and maxima) of a box bounding the + + :param TopoDS_Shape shape: or a subclass such as TopoDS_Face the shape + to compute the bounding box from + :param float tol: tolerance of the computed bounding box + :param bool triangulate: Should shape be triangulated before the + boudning box is created. + + If ``True`` only the dimensions of the bb will take into account + every part of the shape (also not *visible*) + + If ``False`` only the *visible* part is taken into account + + \*See :meth:`~params.FFDParameters.build_bounding_box` + :param float triangulate_tol: tolerance of triangulation (size of + created triangles) + :return: coordinates of minima and maxima along XYZ + :rtype: tuple + """ + bbox = Bnd_Box() + bbox.SetGap(tol) + if triangulate: + BRepMesh_IncrementalMesh(shape, triangulate_tol) + brepbndlib_Add(shape, bbox, triangulate) + xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get() + xyz_min = np.array([xmin, ymin, zmin]) + xyz_max = np.array([xmax, ymax, zmax]) + return xyz_min, xyz_max class RBFParameters(object): - """ - Class that handles the Radial Basis Functions parameters in terms of RBF - control points and basis functions. - - :cvar string basis: name of the basis functions to use in the - transformation. The functions implemented so far are: gaussian spline, - multi quadratic biharmonic spline, inv multi quadratic biharmonic - spline, thin plate spline, beckert wendland c2 basis, polyharmonic - splines. For a comprehensive list with details see the class - :class:`~pygem.radialbasis.RBF`. The default value is None. - :cvar float radius: is the scaling parameter r that affects the shape of the - basis functions. For details see the class - :class:`~pygem.radialbasis.RBF`. The default value is None. - :cvar int n_control_points: total number of control points. - :cvar numpy.ndarray original_control_points: it is an - `n_control_points`-by-3 array with the coordinates of the original - interpolation control points before the deformation. The default value - is None. - :cvar numpy.ndarray deformed_control_points: it is an - `n_control_points`-by-3 array with the coordinates of the - interpolation control points after the deformation. The default value is - None. - """ - def __init__(self): - self.basis = None - self.radius = None - self.power = 2 - self.n_control_points = None - self.original_control_points = None - self.deformed_control_points = None - - - def read_parameters(self, filename='parameters_rbf.prm'): - """ - Reads in the parameters file and fill the self structure. - - :param string filename: parameters file to be read in. Default value is - parameters_rbf.prm. - """ - if not isinstance(filename, str): - raise TypeError('filename must be a string') - - # Checks if the parameters file exists. If not it writes the default - # class into filename. It consists in the vetices of a cube of side one - # with a vertex in (0, 0, 0) and opposite one in (1, 1, 1). - if not os.path.isfile(filename): - self.basis = 'gaussian_spline' - self.radius = 0.5 - self.n_control_points = 8 - self.original_control_points = np.array([0., 0., 0., 0., 0., 1., 0., 1., 0., 1., 0., 0., \ - 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1., 1.]).reshape((8, 3)) - self.deformed_control_points = np.array([0., 0., 0., 0., 0., 1., 0., 1., 0., 1., 0., 0., \ - 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1., 1.]).reshape((8, 3)) - self.write_parameters(filename) - return - - config = configparser.RawConfigParser() - config.read(filename) - - self.basis = config.get('Radial Basis Functions', 'basis function') - self.radius = config.getfloat('Radial Basis Functions', 'radius') - self.power = config.getint('Radial Basis Functions', 'power') - - ctrl_points = config.get('Control points', 'original control points') - lines = ctrl_points.split('\n') - self.n_control_points = len(lines) - self.original_control_points = np.zeros((self.n_control_points, 3)) - for line, i in zip(lines, list(range(0, self.n_control_points))): - values = line.split() - self.original_control_points[i] = np.array([float(values[0]), float(values[1]), float(values[2])]) - - mod_points = config.get('Control points', 'deformed control points') - lines = mod_points.split('\n') - - if len(lines) != self.n_control_points: - raise TypeError("The number of control points must be equal both in the 'original control points'" + \ - " and in the 'deformed control points' section of the parameters file ({0!s})".format(filename)) - - self.deformed_control_points = np.zeros((self.n_control_points, 3)) - for line, i in zip(lines, list(range(0, self.n_control_points))): - values = line.split() - self.deformed_control_points[i] = np.array([float(values[0]), float(values[1]), float(values[2])]) - - - def write_parameters(self, filename='parameters_rbf.prm'): - """ - This method writes a parameters file (.prm) called `filename` and fills - it with all the parameters class members. Default value is - parameters_rbf.prm. - - :param string filename: parameters file to be written out. - """ - if not isinstance(filename, str): - raise TypeError("filename must be a string") - - with open(filename, 'w') as output_file: - output_file.write('\n[Radial Basis Functions]\n') - output_file.write('# This section describes the radial basis functions shape.\n') - - output_file.write('\n# basis funtion is the name of the basis functions to use in the transformation. ' + \ - 'The functions\n') - output_file.write('# implemented so far are: gaussian_spline, multi_quadratic_biharmonic_spline,\n') - output_file.write('# inv_multi_quadratic_biharmonic_spline, thin_plate_spline, beckert_wendland_c2_basis, polyharmonic_spline.\n') - output_file.write('# For a comprehensive list with details see the class RBF.\n') - output_file.write('basis function: ' + str(self.basis) + '\n') - - output_file.write('\n# radius is the scaling parameter r that affects the shape of the basis functions. ' + \ - 'See the documentation\n') - output_file.write('# of the class RBF for details.\n') - output_file.write('radius: ' + str(self.radius) + '\n') - output_file.write('\n# The power parameter k for polyharmonic spline') - output_file.write('\n# See the documentation for details\n') - output_file.write('power: ' + str(self.power) + '\n') - - output_file.write('\n\n[Control points]\n') - output_file.write('# This section describes the RBF control points.\n') - - output_file.write('\n# original control points collects the coordinates of the interpolation ' + \ - 'control points before the deformation.\n') - output_file.write('original control points:') - offset = 1 - for i in range(0, self.n_control_points): - output_file.write(offset * ' ' + str(self.original_control_points[i][0]) + ' ' + \ - str(self.original_control_points[i][1]) + ' ' + \ - str(self.original_control_points[i][2]) + '\n') - offset = 25 - - output_file.write('\n# deformed control points collects the coordinates of the interpolation ' + \ - 'control points after the deformation.\n') - output_file.write('deformed control points:') - offset = 1 - for i in range(0, self.n_control_points): - output_file.write(offset * ' ' + str(self.deformed_control_points[i][0]) + ' ' + \ - str(self.deformed_control_points[i][1]) + ' ' + \ - str(self.deformed_control_points[i][2]) + '\n') - offset = 25 - - - def __str__(self): - """ - This method prints all the RBF parameters on the screen. Its purpose is - for debugging. - """ - string = '' - string += 'basis function = {}\n'.format(self.basis) - string += 'radius = {}\n'.format(self.radius) - string += 'power = {}\n'.format(self.power) - string += '\noriginal control points =\n' - string += '{}\n'.format(self.original_control_points) - string += '\ndeformed control points =\n' - string += '{}\n'.format(self.deformed_control_points) - return string + """ + Class that handles the Radial Basis Functions parameters in terms of RBF + control points and basis functions. + + :cvar string basis: name of the basis functions to use in the + transformation. The functions implemented so far are: gaussian spline, + multi quadratic biharmonic spline, inv multi quadratic biharmonic + spline, thin plate spline, beckert wendland c2 basis, polyharmonic + splines. For a comprehensive list with details see the class + :class:`~pygem.radialbasis.RBF`. The default value is None. + :cvar float radius: is the scaling parameter r that affects the shape of the + basis functions. For details see the class + :class:`~pygem.radialbasis.RBF`. The default value is None. + :cvar int n_control_points: total number of control points. + :cvar numpy.ndarray original_control_points: it is an + `n_control_points`-by-3 array with the coordinates of the original + interpolation control points before the deformation. The default value + is None. + :cvar numpy.ndarray deformed_control_points: it is an + `n_control_points`-by-3 array with the coordinates of the + interpolation control points after the deformation. The default value is + None. + """ + + def __init__(self): + self.basis = None + self.radius = None + self.power = 2 + self.n_control_points = None + self.original_control_points = None + self.deformed_control_points = None + + def read_parameters(self, filename='parameters_rbf.prm'): + """ + Reads in the parameters file and fill the self structure. + + :param string filename: parameters file to be read in. Default value is + parameters_rbf.prm. + """ + if not isinstance(filename, str): + raise TypeError('filename must be a string') + + # Checks if the parameters file exists. If not it writes the default + # class into filename. It consists in the vetices of a cube of side one + # with a vertex in (0, 0, 0) and opposite one in (1, 1, 1). + if not os.path.isfile(filename): + self.basis = 'gaussian_spline' + self.radius = 0.5 + self.n_control_points = 8 + self.original_control_points = np.array([0., 0., 0., 0., 0., 1., 0., 1., 0., 1., 0., 0., \ + 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1., 1.]).reshape((8, 3)) + self.deformed_control_points = np.array([0., 0., 0., 0., 0., 1., 0., 1., 0., 1., 0., 0., \ + 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1., 1.]).reshape((8, 3)) + self.write_parameters(filename) + return + + config = configparser.RawConfigParser() + config.read(filename) + + self.basis = config.get('Radial Basis Functions', 'basis function') + self.radius = config.getfloat('Radial Basis Functions', 'radius') + self.power = config.getint('Radial Basis Functions', 'power') + + ctrl_points = config.get('Control points', 'original control points') + lines = ctrl_points.split('\n') + self.n_control_points = len(lines) + self.original_control_points = np.zeros((self.n_control_points, 3)) + for line, i in zip(lines, list(range(0, self.n_control_points))): + values = line.split() + self.original_control_points[i] = np.array( + [float(values[0]), + float(values[1]), + float(values[2])]) + + mod_points = config.get('Control points', 'deformed control points') + lines = mod_points.split('\n') + + if len(lines) != self.n_control_points: + raise TypeError("The number of control points must be equal both in the 'original control points'" + \ + " and in the 'deformed control points' section of the parameters file ({0!s})".format(filename)) + + self.deformed_control_points = np.zeros((self.n_control_points, 3)) + for line, i in zip(lines, list(range(0, self.n_control_points))): + values = line.split() + self.deformed_control_points[i] = np.array( + [float(values[0]), + float(values[1]), + float(values[2])]) + + def write_parameters(self, filename='parameters_rbf.prm'): + """ + This method writes a parameters file (.prm) called `filename` and fills + it with all the parameters class members. Default value is + parameters_rbf.prm. + + :param string filename: parameters file to be written out. + """ + if not isinstance(filename, str): + raise TypeError("filename must be a string") + + with open(filename, 'w') as output_file: + output_file.write('\n[Radial Basis Functions]\n') + output_file.write( + '# This section describes the radial basis functions shape.\n') + + output_file.write('\n# basis funtion is the name of the basis functions to use in the transformation. ' + \ + 'The functions\n') + output_file.write( + '# implemented so far are: gaussian_spline, multi_quadratic_biharmonic_spline,\n' + ) + output_file.write( + '# inv_multi_quadratic_biharmonic_spline, thin_plate_spline, beckert_wendland_c2_basis, polyharmonic_spline.\n' + ) + output_file.write( + '# For a comprehensive list with details see the class RBF.\n') + output_file.write('basis function: ' + str(self.basis) + '\n') + + output_file.write('\n# radius is the scaling parameter r that affects the shape of the basis functions. ' + \ + 'See the documentation\n') + output_file.write('# of the class RBF for details.\n') + output_file.write('radius: ' + str(self.radius) + '\n') + output_file.write( + '\n# The power parameter k for polyharmonic spline') + output_file.write('\n# See the documentation for details\n') + output_file.write('power: ' + str(self.power) + '\n') + + output_file.write('\n\n[Control points]\n') + output_file.write( + '# This section describes the RBF control points.\n') + + output_file.write('\n# original control points collects the coordinates of the interpolation ' + \ + 'control points before the deformation.\n') + output_file.write('original control points:') + offset = 1 + for i in range(0, self.n_control_points): + output_file.write(offset * ' ' + str(self.original_control_points[i][0]) + ' ' + \ + str(self.original_control_points[i][1]) + ' ' + \ + str(self.original_control_points[i][2]) + '\n') + offset = 25 + + output_file.write('\n# deformed control points collects the coordinates of the interpolation ' + \ + 'control points after the deformation.\n') + output_file.write('deformed control points:') + offset = 1 + for i in range(0, self.n_control_points): + output_file.write(offset * ' ' + str(self.deformed_control_points[i][0]) + ' ' + \ + str(self.deformed_control_points[i][1]) + ' ' + \ + str(self.deformed_control_points[i][2]) + '\n') + offset = 25 + + def __str__(self): + """ + This method prints all the RBF parameters on the screen. Its purpose is + for debugging. + """ + string = '' + string += 'basis function = {}\n'.format(self.basis) + string += 'radius = {}\n'.format(self.radius) + string += 'power = {}\n'.format(self.power) + string += '\noriginal control points =\n' + string += '{}\n'.format(self.original_control_points) + string += '\ndeformed control points =\n' + string += '{}\n'.format(self.deformed_control_points) + return string diff --git a/pygem/params_idw.py b/pygem/params_idw.py index 3f313e2..8b4393b 100644 --- a/pygem/params_idw.py +++ b/pygem/params_idw.py @@ -5,127 +5,106 @@ except ImportError: import ConfigParser as configparser + class IDWParameters(object): - """ - Class that handles the Inverse Distance Weighting parameters in terms of - control points. - - :cvar int power: the power parameter. The default value is 2. - :cvar numpy.ndarray original_control_points: it is an - `n_control_points`-by-3 array with the coordinates of the original - interpolation control points before the deformation. The default is the - vertices of the unit cube. - :cvar numpy.ndarray deformed_control_points: it is an - `n_control_points`-by-3 array with the coordinates of the interpolation - control points after the deformation. The default is the vertices of - the unit cube. - """ - def __init__(self): - self.power = 2 - self.original_control_points = np.array([ - [0., 0., 0.], - [0., 0., 1.], - [0., 1., 0.], - [1., 0., 0.], - [0., 1., 1.], - [1., 0., 1.], - [1., 1., 0.], - [1., 1., 1.] - ]) - self.deformed_control_points = np.array([ - [0., 0., 0.], - [0., 0., 1.], - [0., 1., 0.], - [1., 0., 0.], - [0., 1., 1.], - [1., 0., 1.], - [1., 1., 0.], - [1., 1., 1.] - ]) - - - def read_parameters(self, filename): - """ - Reads in the parameters file and fill the self structure. - - :param string filename: parameters file to be read in. - """ - if not isinstance(filename, str): - raise TypeError('filename must be a string') - - if not os.path.isfile(filename): - raise IOError('filename does not exist') - - config = configparser.RawConfigParser() - config.read(filename) - - self.power = config.getint('Inverse Distance Weighting', 'power') - - ctrl_points = config.get('Control points', 'original control points') - self.original_control_points = np.array( - [line.split() for line in ctrl_points.split('\n')], - dtype=float - ) - - defo_points = config.get('Control points', 'deformed control points') - self.deformed_control_points = np.array( - [line.split() for line in defo_points.split('\n')], - dtype=float - ) - - - def write_parameters(self, filename): - """ - This method writes a parameters file (.prm) called `filename` and fills - it with all the parameters class members. - - :param string filename: parameters file to be written out. - """ - if not isinstance(filename, str): - raise TypeError("filename must be a string") - - output_string = "" - output_string += "\n[Inverse Distance Weighting]\n" - output_string += "# This section describes the settings of idw.\n\n" - output_string += "# the power parameter\n" - output_string += "power = {}\n".format(self.power) - - output_string += "\n\n[Control points]\n" - output_string += "# This section describes the IDW control points.\n\n" - output_string += "# original control points collects the coordinates\n" - output_string += "# of the interpolation control points before the\n" - output_string += "# deformation.\n" - - output_string += "original control points: " - output_string += ( - ' '.join(map(str, self.original_control_points[0])) + "\n" - ) - for points in self.original_control_points[1:]: - output_string += 25 * ' ' + ' '.join(map(str, points)) + "\n" - output_string += "\n" - output_string += "# deformed control points collects the coordinates\n" - output_string += "# of the interpolation control points after the\n" - output_string += "# deformation.\n" - output_string += "deformed control points: " - output_string += ( - ' '.join(map(str, self.original_control_points[0])) + "\n" - ) - for points in self.deformed_control_points[1:]: - output_string += 25 * ' ' + ' '.join(map(str, points)) + "\n" - - with open(filename, 'w') as f: - f.write(output_string) - - - def __str__(self): - """ - This method prints all the IDW parameters on the screen. Its purpose is - for debugging. - """ - string = '' - string += 'p = {}\n'.format(self.power) - string += '\noriginal_control_points =\n' - string += '{}\n'.format(self.original_control_points) - string += '\ndeformed_control_points =\n' - string += '{}\n'.format(self.deformed_control_points) - return string + """ + Class that handles the Inverse Distance Weighting parameters in terms of + control points. + + :cvar int power: the power parameter. The default value is 2. + :cvar numpy.ndarray original_control_points: it is an + `n_control_points`-by-3 array with the coordinates of the original + interpolation control points before the deformation. The default is the + vertices of the unit cube. + :cvar numpy.ndarray deformed_control_points: it is an + `n_control_points`-by-3 array with the coordinates of the interpolation + control points after the deformation. The default is the vertices of + the unit cube. + """ + + def __init__(self): + self.power = 2 + self.original_control_points = np.array( + [[0., 0., 0.], [0., 0., 1.], [0., 1., 0.], [1., 0., 0.], + [0., 1., 1.], [1., 0., 1.], [1., 1., 0.], [1., 1., 1.]]) + self.deformed_control_points = np.array( + [[0., 0., 0.], [0., 0., 1.], [0., 1., 0.], [1., 0., 0.], + [0., 1., 1.], [1., 0., 1.], [1., 1., 0.], [1., 1., 1.]]) + + def read_parameters(self, filename): + """ + Reads in the parameters file and fill the self structure. + + :param string filename: parameters file to be read in. + """ + if not isinstance(filename, str): + raise TypeError('filename must be a string') + + if not os.path.isfile(filename): + raise IOError('filename does not exist') + + config = configparser.RawConfigParser() + config.read(filename) + + self.power = config.getint('Inverse Distance Weighting', 'power') + + ctrl_points = config.get('Control points', 'original control points') + self.original_control_points = np.array( + [line.split() for line in ctrl_points.split('\n')], dtype=float) + + defo_points = config.get('Control points', 'deformed control points') + self.deformed_control_points = np.array( + [line.split() for line in defo_points.split('\n')], dtype=float) + + def write_parameters(self, filename): + """ + This method writes a parameters file (.prm) called `filename` and fills + it with all the parameters class members. + + :param string filename: parameters file to be written out. + """ + if not isinstance(filename, str): + raise TypeError("filename must be a string") + + output_string = "" + output_string += "\n[Inverse Distance Weighting]\n" + output_string += "# This section describes the settings of idw.\n\n" + output_string += "# the power parameter\n" + output_string += "power = {}\n".format(self.power) + + output_string += "\n\n[Control points]\n" + output_string += "# This section describes the IDW control points.\n\n" + output_string += "# original control points collects the coordinates\n" + output_string += "# of the interpolation control points before the\n" + output_string += "# deformation.\n" + + output_string += "original control points: " + output_string += ( + ' '.join(map(str, self.original_control_points[0])) + "\n") + for points in self.original_control_points[1:]: + output_string += 25 * ' ' + ' '.join(map(str, points)) + "\n" + output_string += "\n" + output_string += "# deformed control points collects the coordinates\n" + output_string += "# of the interpolation control points after the\n" + output_string += "# deformation.\n" + output_string += "deformed control points: " + output_string += ( + ' '.join(map(str, self.original_control_points[0])) + "\n") + for points in self.deformed_control_points[1:]: + output_string += 25 * ' ' + ' '.join(map(str, points)) + "\n" + + with open(filename, 'w') as f: + f.write(output_string) + + def __str__(self): + """ + This method prints all the IDW parameters on the screen. Its purpose is + for debugging. + """ + string = '' + string += 'p = {}\n'.format(self.power) + string += '\noriginal_control_points =\n' + string += '{}\n'.format(self.original_control_points) + string += '\ndeformed_control_points =\n' + string += '{}\n'.format(self.deformed_control_points) + return string diff --git a/pygem/radial.py b/pygem/radial.py index 3f94b8e..f3d1075 100644 --- a/pygem/radial.py +++ b/pygem/radial.py @@ -10,50 +10,50 @@ :Theoretical Insight: - As reference please consult M.D. Buhmann, Radial Basis Functions, volume 12 - of Cambridge monographs on applied and computational mathematics. Cambridge - University Press, UK, 2003. This implementation follows D. Forti and G. - Rozza, Efficient geometrical parametrization techniques of interfaces for - reduced order modelling: application to fluid-structure interaction coupling - problems, International Journal of Computational Fluid Dynamics. - - RBF shape parametrization technique is based on the definition of a map, - :math:`\\mathcal{M}(\\boldsymbol{x}) : \\mathbb{R}^n \\rightarrow - \\mathbb{R}^n`, that allows the possibility of transferring data across - non-matching grids and facing the dynamic mesh handling. The map introduced - is defines as follows - - .. math:: - \\mathcal{M}(\\boldsymbol{x}) = p(\\boldsymbol{x}) + - \\sum_{i=1}^{\\mathcal{N}_C} \\gamma_i - \\varphi(\\| \\boldsymbol{x} - \\boldsymbol{x_{C_i}} \\|) - - where :math:`p(\\boldsymbol{x})` is a low_degree polynomial term, - :math:`\\gamma_i` is the weight, corresponding to the a-priori selected - :math:`\\mathcal{N}_C` control points, associated to the :math:`i`-th basis - function, and :math:`\\varphi(\\| \\boldsymbol{x} - \\boldsymbol{x_{C_i}} - \\|)` a radial function based on the Euclidean distance between the - control points position :math:`\\boldsymbol{x_{C_i}}` and - :math:`\\boldsymbol{x}`. A radial basis function, generally, is a - real-valued function whose value depends only on the distance from the - origin, so that :math:`\\varphi(\\boldsymbol{x}) = \\tilde{\\varphi}(\\| - \\boldsymbol{x} \\|)`. - - The matrix version of the formula above is: - - .. math:: - \\mathcal{M}(\\boldsymbol{x}) = \\boldsymbol{c} + - \\boldsymbol{Q}\\boldsymbol{x} + - \\boldsymbol{W^T}\\boldsymbol{d}(\\boldsymbol{x}) - - The idea is that after the computation of the weights and the polynomial - terms from the coordinates of the control points before and after the - deformation, we can deform all the points of the mesh accordingly. Among - the most common used radial basis functions for modelling 2D and 3D shapes, - we consider Gaussian splines, Multi-quadratic biharmonic splines, Inverted - multi-quadratic biharmonic splines, Thin-plate splines, Beckert and - Wendland :math:`C^2` basis and Polyharmonic splines all defined and - implemented below. + As reference please consult M.D. Buhmann, Radial Basis Functions, volume 12 + of Cambridge monographs on applied and computational mathematics. Cambridge + University Press, UK, 2003. This implementation follows D. Forti and G. + Rozza, Efficient geometrical parametrization techniques of interfaces for + reduced order modelling: application to fluid-structure interaction coupling + problems, International Journal of Computational Fluid Dynamics. + + RBF shape parametrization technique is based on the definition of a map, + :math:`\\mathcal{M}(\\boldsymbol{x}) : \\mathbb{R}^n \\rightarrow + \\mathbb{R}^n`, that allows the possibility of transferring data across + non-matching grids and facing the dynamic mesh handling. The map introduced + is defines as follows + + .. math:: + \\mathcal{M}(\\boldsymbol{x}) = p(\\boldsymbol{x}) + + \\sum_{i=1}^{\\mathcal{N}_C} \\gamma_i + \\varphi(\\| \\boldsymbol{x} - \\boldsymbol{x_{C_i}} \\|) + + where :math:`p(\\boldsymbol{x})` is a low_degree polynomial term, + :math:`\\gamma_i` is the weight, corresponding to the a-priori selected + :math:`\\mathcal{N}_C` control points, associated to the :math:`i`-th basis + function, and :math:`\\varphi(\\| \\boldsymbol{x} - \\boldsymbol{x_{C_i}} + \\|)` a radial function based on the Euclidean distance between the + control points position :math:`\\boldsymbol{x_{C_i}}` and + :math:`\\boldsymbol{x}`. A radial basis function, generally, is a + real-valued function whose value depends only on the distance from the + origin, so that :math:`\\varphi(\\boldsymbol{x}) = \\tilde{\\varphi}(\\| + \\boldsymbol{x} \\|)`. + + The matrix version of the formula above is: + + .. math:: + \\mathcal{M}(\\boldsymbol{x}) = \\boldsymbol{c} + + \\boldsymbol{Q}\\boldsymbol{x} + + \\boldsymbol{W^T}\\boldsymbol{d}(\\boldsymbol{x}) + + The idea is that after the computation of the weights and the polynomial + terms from the coordinates of the control points before and after the + deformation, we can deform all the points of the mesh accordingly. Among + the most common used radial basis functions for modelling 2D and 3D shapes, + we consider Gaussian splines, Multi-quadratic biharmonic splines, Inverted + multi-quadratic biharmonic splines, Thin-plate splines, Beckert and + Wendland :math:`C^2` basis and Polyharmonic splines all defined and + implemented below. """ import numpy as np @@ -61,279 +61,280 @@ class RBF(object): - """ - Class that handles the Radial Basis Functions interpolation on the mesh - points. - - :param RBFParameters rbf_parameters: parameters of the RBF. - :param numpy.ndarray original_mesh_points: coordinates of the original - points of the mesh. - :cvar RBFParameters parameters: parameters of the RBF. - :cvar numpy.ndarray original_mesh_points: coordinates of the original points - of the mesh. The shape is `n_points`-by-3. - :cvar numpy.ndarray modified_mesh_points: coordinates of the points of the - deformed mesh. The shape is `n_points`-by-3. - :cvar dict bases: a dictionary that associates the names of the basis - functions implemented to the actual implementation. - :cvar numpy.matrix weights: the matrix formed by the weights corresponding - to the a-priori selected N control points, associated to the basis - functions and c and Q terms that describe the polynomial of order one - p(x) = c + Qx. The shape is (n_control_points+1+3)-by-3. It is computed - internally. - - :Example: - - >>> import pygem.radial as rbf - >>> import pygem.params as rbfp - >>> import numpy as np - >>> rbf_parameters = rbfp.RBFParameters() - >>> rbf_parameters.read_parameters('tests/test_datasets/parameters_rbf_cube.prm') - >>> nx, ny, nz = (20, 20, 20) - >>> mesh = np.zeros((nx * ny * nz, 3)) - >>> xv = np.linspace(0, 1, nx) - >>> yv = np.linspace(0, 1, ny) - >>> zv = np.linspace(0, 1, nz) - >>> z, y, x = np.meshgrid(zv, yv, xv) - >>> mesh = np.array([x.ravel(), y.ravel(), z.ravel()]) - >>> original_mesh_points = mesh.T - >>> radial_trans = rbf.RBF(rbf_parameters, original_mesh_points) - >>> radial_trans.perform() - >>> new_mesh_points = radial_trans.modified_mesh_points - """ - - def __init__(self, rbf_parameters, original_mesh_points): - self.parameters = rbf_parameters - self.original_mesh_points = original_mesh_points - self.modified_mesh_points = None - - self.bases = { - 'gaussian_spline': self.gaussian_spline, - 'multi_quadratic_biharmonic_spline': - self.multi_quadratic_biharmonic_spline, - 'inv_multi_quadratic_biharmonic_spline': - self.inv_multi_quadratic_biharmonic_spline, - 'thin_plate_spline': self.thin_plate_spline, - 'beckert_wendland_c2_basis': self.beckert_wendland_c2_basis, - 'polyharmonic_spline': self.polyharmonic_spline - } - - # to make the str callable we have to use a dictionary with all the - # implemented radial basis functions - if self.parameters.basis in self.bases: - self.basis = self.bases[self.parameters.basis] - else: - raise NameError( - """The name of the basis function in the parameters file is not - correct or not implemented. Check the documentation for - all the available functions.""" - ) - - self.weights = self._get_weights( - self.parameters.original_control_points, - self.parameters.deformed_control_points - ) - - @staticmethod - def gaussian_spline(X, r): - """ - It implements the following formula: - - .. math:: - \\varphi(\\| \\boldsymbol{x} \\|) = - e^{-\\frac{\\| \\boldsymbol{x} \\|^2}{r^2}} - - :param numpy.ndarray X: the vector x in the formula above. - :param float r: the parameter r in the formula above. - - :return: result: the result of the formula above. - :rtype: float - """ - norm = np.linalg.norm(X) - result = np.exp(-(norm * norm) / (r * r)) - return result - - @staticmethod - def multi_quadratic_biharmonic_spline(X, r): - """ - It implements the following formula: - - .. math:: - \\varphi(\\| \\boldsymbol{x} \\|) = - \\sqrt{\\| \\boldsymbol{x} \\|^2 + r^2} - - :param numpy.ndarray X: the vector x in the formula above. - :param float r: the parameter r in the formula above. - - :return: result: the result of the formula above. - :rtype: float - """ - norm = np.linalg.norm(X) - result = np.sqrt((norm * norm) + (r * r)) - return result - - @staticmethod - def inv_multi_quadratic_biharmonic_spline(X, r): - """ - It implements the following formula: - - .. math:: - \\varphi(\\| \\boldsymbol{x} \\|) = - (\\| \\boldsymbol{x} \\|^2 + r^2 )^{-\\frac{1}{2}} - - :param numpy.ndarray X: the vector x in the formula above. - :param float r: the parameter r in the formula above. - - :return: result: the result of the formula above. - :rtype: float - """ - norm = np.linalg.norm(X) - result = 1.0 / (np.sqrt((norm * norm) + (r * r))) - return result - - @staticmethod - def thin_plate_spline(X, r): - """ - It implements the following formula: - - .. math:: - \\varphi(\\| \\boldsymbol{x} \\|) = - \\left\\| \\frac{\\boldsymbol{x} }{r} \\right\\|^2 - \\ln \\left\\| \\frac{\\boldsymbol{x} }{r} \\right\\| - - :param numpy.ndarray X: the vector x in the formula above. - :param float r: the parameter r in the formula above. - - :return: result: the result of the formula above. - :rtype: float - """ - arg = X / r - norm = np.linalg.norm(arg) - result = norm * norm - if norm > 0: - result *= np.log(norm) - return result - - @staticmethod - def beckert_wendland_c2_basis(X, r): - """ - It implements the following formula: - - .. math:: - \\varphi(\\| \\boldsymbol{x} \\|) = - \\left( 1 - \\frac{\\| \\boldsymbol{x} \\|}{r} \\right)^4_+ - \\left( 4 \\frac{\\| \\boldsymbol{x} \\|}{r} + 1 \\right) - - :param numpy.ndarray X: the vector x in the formula above. - :param float r: the parameter r in the formula above. - - :return: result: the result of the formula above. - :rtype: float - """ - norm = np.linalg.norm(X) - arg = norm / r - first = 0 - if (1 - arg) > 0: - first = np.power((1 - arg), 4) - second = (4 * arg) + 1 - result = first * second - return result - - def polyharmonic_spline(self, X, r): - """ - It implements the following formula: - - .. math:: - - \\varphi(\\| \\boldsymbol{x} \\|) = - \\begin{cases} - \\|\\frac{\\boldsymbol{x}}{r}\\|^k - \\quad & \\text{if}~k = 1,3,5,...\\\\ - \\|\\frac{\\boldsymbol{x}}{r}\\|^{k-1} - \\ln(\\|\\frac{\\boldsymbol{x}}{r}\\|^ - {\\|\\frac{\\boldsymbol{x}}{r}\\|}) - \\quad & \\text{if}~\\|\\frac{\\boldsymbol{x}}{r}\\| < 1, - ~k = 2,4,6,...\\\\ - \\|\\frac{\\boldsymbol{x}}{r}\\|^k - \\ln(\\|\\frac{\\boldsymbol{x}}{r}\\|) - \\quad & \\text{if}~\\|\\frac{\\boldsymbol{x}}{r}\\| \\ge 1, - ~k = 2,4,6,...\\\\ - \\end{cases} - - :param numpy.ndarray X: the vector x in the formula above. - :param float r: the parameter r in the formula above. - - :return: result: the result of the formula above. - :rtype: float - """ - - k = self.parameters.power - r_sc = np.linalg.norm(X) / r - power = np.power - - if k & 1: return power(r_sc, k) # k odd - - # Here, k is even - if r_sc < 1: - return power(r_sc, k-1) * np.log(power(r_sc, r_sc)) - else : - return power(r_sc, k) * np.log(r_sc) - - def _distance_matrix(self, X1, X2): - """ - This private method returns the following matrix: - :math:`\\boldsymbol{D_{ij}} = \\varphi(\\| \\boldsymbol{x_i} - - \\boldsymbol{y_j} \\|)` - - :param numpy.ndarray X1: the vector x in the formula above. - :param numpy.ndarray X2: the vector y in the formula above. - - :return: matrix: the matrix D. - :rtype: numpy.ndarray - """ - matrix = cdist( - X1, X2, lambda x, y: self.basis(x-y, self.parameters.radius) - ) - return matrix - - def _get_weights(self, X, Y): - """ - This private method, given the original control points and the deformed - ones, returns the matrix with the weights and the polynomial terms, that - is :math:`W`, :math:`c^T` and :math:`Q^T`. The shape is - (n_control_points+1+3)-by-3. - - :param numpy.ndarray X: it is an n_control_points-by-3 array with the - coordinates of the original interpolation control points before the - deformation. - :param numpy.ndarray Y: it is an n_control_points-by-3 array with the - coordinates of the interpolation control points after the - deformation. - - :return: weights: the matrix with the weights and the polynomial terms. - :rtype: numpy.matrix - """ - n_points = X.shape[0] - dim = X.shape[1] - identity = np.ones((n_points, 1)) - dist = self._distance_matrix(X, X) - H = np.bmat([ - [dist, identity, X], - [identity.T, np.zeros((1, 1)), np.zeros((1, dim))], - [X.T, np.zeros((dim, 1)), np.zeros((dim, dim))] - ]) - rhs = np.bmat([[Y], [np.zeros((1, dim))], [np.zeros((dim, dim))]]) - weights = np.linalg.solve(H, rhs) - return weights - - def perform(self): - """ - This method performs the deformation of the mesh points. After the - execution it sets `self.modified_mesh_points`. - """ - n_points = self.original_mesh_points.shape[0] - dist = self._distance_matrix( - self.original_mesh_points, self.parameters.original_control_points - ) - identity = np.ones((n_points, 1)) - H = np.bmat([[dist, identity, self.original_mesh_points]]) - self.modified_mesh_points = np.asarray(np.dot(H, self.weights)) + """ + Class that handles the Radial Basis Functions interpolation on the mesh + points. + + :param RBFParameters rbf_parameters: parameters of the RBF. + :param numpy.ndarray original_mesh_points: coordinates of the original + points of the mesh. + :cvar RBFParameters parameters: parameters of the RBF. + :cvar numpy.ndarray original_mesh_points: coordinates of the original points + of the mesh. The shape is `n_points`-by-3. + :cvar numpy.ndarray modified_mesh_points: coordinates of the points of the + deformed mesh. The shape is `n_points`-by-3. + :cvar dict bases: a dictionary that associates the names of the basis + functions implemented to the actual implementation. + :cvar numpy.matrix weights: the matrix formed by the weights corresponding + to the a-priori selected N control points, associated to the basis + functions and c and Q terms that describe the polynomial of order one + p(x) = c + Qx. The shape is (n_control_points+1+3)-by-3. It is computed + internally. + + :Example: + + >>> import pygem.radial as rbf + >>> import pygem.params as rbfp + >>> import numpy as np + >>> rbf_parameters = rbfp.RBFParameters() + >>> rbf_parameters.read_parameters('tests/test_datasets/parameters_rbf_cube.prm') + >>> nx, ny, nz = (20, 20, 20) + >>> mesh = np.zeros((nx * ny * nz, 3)) + >>> xv = np.linspace(0, 1, nx) + >>> yv = np.linspace(0, 1, ny) + >>> zv = np.linspace(0, 1, nz) + >>> z, y, x = np.meshgrid(zv, yv, xv) + >>> mesh = np.array([x.ravel(), y.ravel(), z.ravel()]) + >>> original_mesh_points = mesh.T + >>> radial_trans = rbf.RBF(rbf_parameters, original_mesh_points) + >>> radial_trans.perform() + >>> new_mesh_points = radial_trans.modified_mesh_points + """ + + def __init__(self, rbf_parameters, original_mesh_points): + self.parameters = rbf_parameters + self.original_mesh_points = original_mesh_points + self.modified_mesh_points = None + + self.bases = { + 'gaussian_spline': + self.gaussian_spline, + 'multi_quadratic_biharmonic_spline': + self.multi_quadratic_biharmonic_spline, + 'inv_multi_quadratic_biharmonic_spline': + self.inv_multi_quadratic_biharmonic_spline, + 'thin_plate_spline': + self.thin_plate_spline, + 'beckert_wendland_c2_basis': + self.beckert_wendland_c2_basis, + 'polyharmonic_spline': + self.polyharmonic_spline + } + + # to make the str callable we have to use a dictionary with all the + # implemented radial basis functions + if self.parameters.basis in self.bases: + self.basis = self.bases[self.parameters.basis] + else: + raise NameError( + """The name of the basis function in the parameters file is not + correct or not implemented. Check the documentation for + all the available functions.""") + + self.weights = self._get_weights( + self.parameters.original_control_points, + self.parameters.deformed_control_points) + + @staticmethod + def gaussian_spline(X, r): + """ + It implements the following formula: + + .. math:: + \\varphi(\\| \\boldsymbol{x} \\|) = + e^{-\\frac{\\| \\boldsymbol{x} \\|^2}{r^2}} + + :param numpy.ndarray X: the vector x in the formula above. + :param float r: the parameter r in the formula above. + + :return: result: the result of the formula above. + :rtype: float + """ + norm = np.linalg.norm(X) + result = np.exp(-(norm * norm) / (r * r)) + return result + + @staticmethod + def multi_quadratic_biharmonic_spline(X, r): + """ + It implements the following formula: + + .. math:: + \\varphi(\\| \\boldsymbol{x} \\|) = + \\sqrt{\\| \\boldsymbol{x} \\|^2 + r^2} + + :param numpy.ndarray X: the vector x in the formula above. + :param float r: the parameter r in the formula above. + + :return: result: the result of the formula above. + :rtype: float + """ + norm = np.linalg.norm(X) + result = np.sqrt((norm * norm) + (r * r)) + return result + + @staticmethod + def inv_multi_quadratic_biharmonic_spline(X, r): + """ + It implements the following formula: + + .. math:: + \\varphi(\\| \\boldsymbol{x} \\|) = + (\\| \\boldsymbol{x} \\|^2 + r^2 )^{-\\frac{1}{2}} + + :param numpy.ndarray X: the vector x in the formula above. + :param float r: the parameter r in the formula above. + + :return: result: the result of the formula above. + :rtype: float + """ + norm = np.linalg.norm(X) + result = 1.0 / (np.sqrt((norm * norm) + (r * r))) + return result + + @staticmethod + def thin_plate_spline(X, r): + """ + It implements the following formula: + + .. math:: + \\varphi(\\| \\boldsymbol{x} \\|) = + \\left\\| \\frac{\\boldsymbol{x} }{r} \\right\\|^2 + \\ln \\left\\| \\frac{\\boldsymbol{x} }{r} \\right\\| + + :param numpy.ndarray X: the vector x in the formula above. + :param float r: the parameter r in the formula above. + + :return: result: the result of the formula above. + :rtype: float + """ + arg = X / r + norm = np.linalg.norm(arg) + result = norm * norm + if norm > 0: + result *= np.log(norm) + return result + + @staticmethod + def beckert_wendland_c2_basis(X, r): + """ + It implements the following formula: + + .. math:: + \\varphi(\\| \\boldsymbol{x} \\|) = + \\left( 1 - \\frac{\\| \\boldsymbol{x} \\|}{r} \\right)^4_+ + \\left( 4 \\frac{\\| \\boldsymbol{x} \\|}{r} + 1 \\right) + + :param numpy.ndarray X: the vector x in the formula above. + :param float r: the parameter r in the formula above. + + :return: result: the result of the formula above. + :rtype: float + """ + norm = np.linalg.norm(X) + arg = norm / r + first = 0 + if (1 - arg) > 0: + first = np.power((1 - arg), 4) + second = (4 * arg) + 1 + result = first * second + return result + + def polyharmonic_spline(self, X, r): + """ + It implements the following formula: + + .. math:: + + \\varphi(\\| \\boldsymbol{x} \\|) = + \\begin{cases} + \\|\\frac{\\boldsymbol{x}}{r}\\|^k + \\quad & \\text{if}~k = 1,3,5,...\\\\ + \\|\\frac{\\boldsymbol{x}}{r}\\|^{k-1} + \\ln(\\|\\frac{\\boldsymbol{x}}{r}\\|^ + {\\|\\frac{\\boldsymbol{x}}{r}\\|}) + \\quad & \\text{if}~\\|\\frac{\\boldsymbol{x}}{r}\\| < 1, + ~k = 2,4,6,...\\\\ + \\|\\frac{\\boldsymbol{x}}{r}\\|^k + \\ln(\\|\\frac{\\boldsymbol{x}}{r}\\|) + \\quad & \\text{if}~\\|\\frac{\\boldsymbol{x}}{r}\\| \\ge 1, + ~k = 2,4,6,...\\\\ + \\end{cases} + + :param numpy.ndarray X: the vector x in the formula above. + :param float r: the parameter r in the formula above. + + :return: result: the result of the formula above. + :rtype: float + """ + + k = self.parameters.power + r_sc = np.linalg.norm(X) / r + power = np.power + + if k & 1: return power(r_sc, k) # k odd + + # Here, k is even + if r_sc < 1: + return power(r_sc, k - 1) * np.log(power(r_sc, r_sc)) + else: + return power(r_sc, k) * np.log(r_sc) + + def _distance_matrix(self, X1, X2): + """ + This private method returns the following matrix: + :math:`\\boldsymbol{D_{ij}} = \\varphi(\\| \\boldsymbol{x_i} - + \\boldsymbol{y_j} \\|)` + + :param numpy.ndarray X1: the vector x in the formula above. + :param numpy.ndarray X2: the vector y in the formula above. + + :return: matrix: the matrix D. + :rtype: numpy.ndarray + """ + matrix = cdist(X1, X2, + lambda x, y: self.basis(x - y, self.parameters.radius)) + return matrix + + def _get_weights(self, X, Y): + """ + This private method, given the original control points and the deformed + ones, returns the matrix with the weights and the polynomial terms, that + is :math:`W`, :math:`c^T` and :math:`Q^T`. The shape is + (n_control_points+1+3)-by-3. + + :param numpy.ndarray X: it is an n_control_points-by-3 array with the + coordinates of the original interpolation control points before the + deformation. + :param numpy.ndarray Y: it is an n_control_points-by-3 array with the + coordinates of the interpolation control points after the + deformation. + + :return: weights: the matrix with the weights and the polynomial terms. + :rtype: numpy.matrix + """ + n_points = X.shape[0] + dim = X.shape[1] + identity = np.ones((n_points, 1)) + dist = self._distance_matrix(X, X) + H = np.bmat([[dist, identity, + X], [identity.T, + np.zeros((1, 1)), + np.zeros((1, dim))], + [X.T, np.zeros((dim, 1)), + np.zeros((dim, dim))]]) + rhs = np.bmat([[Y], [np.zeros((1, dim))], [np.zeros((dim, dim))]]) + weights = np.linalg.solve(H, rhs) + return weights + + def perform(self): + """ + This method performs the deformation of the mesh points. After the + execution it sets `self.modified_mesh_points`. + """ + n_points = self.original_mesh_points.shape[0] + dist = self._distance_matrix(self.original_mesh_points, + self.parameters.original_control_points) + identity = np.ones((n_points, 1)) + H = np.bmat([[dist, identity, self.original_mesh_points]]) + self.modified_mesh_points = np.asarray(np.dot(H, self.weights)) diff --git a/pygem/stephandler.py b/pygem/stephandler.py index c33cd9d..c36b038 100644 --- a/pygem/stephandler.py +++ b/pygem/stephandler.py @@ -10,72 +10,72 @@ class StepHandler(NurbsHandler): - """ - Step file handler class + """ + Step file handler class - :cvar string infile: name of the input file to be processed. - :cvar string outfile: name of the output file where to write in. - :cvar list extensions: list of extensions of the input/output files. - It is equal to ['.step', '.stp']. - :cvar list control_point_position: index of the first NURBS control point (or pole) - of each face of the iges file. - :cvar float tolerance: tolerance for the construction of the faces and wires - in the write function. Default value is 1e-6. - :cvar TopoDS_Shape shape: shape meant for modification. + :cvar string infile: name of the input file to be processed. + :cvar string outfile: name of the output file where to write in. + :cvar list extensions: list of extensions of the input/output files. + It is equal to ['.step', '.stp']. + :cvar list control_point_position: index of the first NURBS control point (or pole) + of each face of the iges file. + :cvar float tolerance: tolerance for the construction of the faces and wires + in the write function. Default value is 1e-6. + :cvar TopoDS_Shape shape: shape meant for modification. - .. warning:: + .. warning:: - - For non trivial geometries it could be necessary to increase the tolerance. - Linking edges into a single wire and then trimming the surface with the wire - can be hard for the software, especially when the starting CAD has not been - made for analysis but for design purposes. - """ + - For non trivial geometries it could be necessary to increase the tolerance. + Linking edges into a single wire and then trimming the surface with the wire + can be hard for the software, especially when the starting CAD has not been + made for analysis but for design purposes. + """ - def __init__(self): - super(StepHandler, self).__init__() - self._control_point_position = None - self.extensions = ['.step', '.stp'] + def __init__(self): + super(StepHandler, self).__init__() + self._control_point_position = None + self.extensions = ['.step', '.stp'] - def load_shape_from_file(self, filename): - """ - This method loads a shape from the file `filename`. + def load_shape_from_file(self, filename): + """ + This method loads a shape from the file `filename`. - :param string filename: name of the input file. - It should have proper extension (.step or .stp) + :param string filename: name of the input file. + It should have proper extension (.step or .stp) - :return: shape: loaded shape - :rtype: TopoDS_Shape - """ - self._check_filename_type(filename) - self._check_extension(filename) - reader = STEPControl_Reader() - return_reader = reader.ReadFile(filename) - # check status - if return_reader == IFSelect_RetDone: - return_transfer = reader.TransferRoots() - if return_transfer: - # load all shapes in one - shape = reader.OneShape() - return shape - else: - raise RuntimeError("Shapes not loaded.") - else: - raise RuntimeError("Cannot read the file.") + :return: shape: loaded shape + :rtype: TopoDS_Shape + """ + self._check_filename_type(filename) + self._check_extension(filename) + reader = STEPControl_Reader() + return_reader = reader.ReadFile(filename) + # check status + if return_reader == IFSelect_RetDone: + return_transfer = reader.TransferRoots() + if return_transfer: + # load all shapes in one + shape = reader.OneShape() + return shape + else: + raise RuntimeError("Shapes not loaded.") + else: + raise RuntimeError("Cannot read the file.") - def write_shape_to_file(self, shape, filename): - """ - This method saves the `shape` to the file `filename`. + def write_shape_to_file(self, shape, filename): + """ + This method saves the `shape` to the file `filename`. - :param: TopoDS_Shape shape: loaded shape - :param string filename: name of the input file. - It should have proper extension (.step or .stp) - """ - self._check_filename_type(filename) - self._check_extension(filename) - step_writer = STEPControl_Writer() - # Changes write schema to STEP standard AP203 - # It is considered the most secure standard for STEP. - # *According to PythonOCC documentation (http://www.pythonocc.org/) - Interface_Static_SetCVal("write.step.schema", "AP203") - step_writer.Transfer(shape, STEPControl_AsIs) - step_writer.Write(filename) + :param: TopoDS_Shape shape: loaded shape + :param string filename: name of the input file. + It should have proper extension (.step or .stp) + """ + self._check_filename_type(filename) + self._check_extension(filename) + step_writer = STEPControl_Writer() + # Changes write schema to STEP standard AP203 + # It is considered the most secure standard for STEP. + # *According to PythonOCC documentation (http://www.pythonocc.org/) + Interface_Static_SetCVal("write.step.schema", "AP203") + step_writer.Transfer(shape, STEPControl_AsIs) + step_writer.Write(filename) diff --git a/pygem/stlhandler.py b/pygem/stlhandler.py index 50beba3..d247533 100644 --- a/pygem/stlhandler.py +++ b/pygem/stlhandler.py @@ -10,212 +10,203 @@ class StlHandler(fh.FileHandler): - """ - STereoLithography file handler class - - :cvar string infile: name of the input file to be processed. - :cvar string outfile: name of the output file where to write in. - :cvar list extensions: extensions of the input/output files. It is equal to - ['.stl']. - """ - - def __init__(self): - super(StlHandler, self).__init__() - self.extensions = ['.stl'] - - - def parse(self, filename): - """ - Method to parse the `filename`. It returns a matrix with all the - coordinates. - - :param string filename: name of the input file. - - :return: mesh_points: it is a `n_points`-by-3 matrix containing the - coordinates of the points of the mesh - :rtype: numpy.ndarray - """ - self._check_filename_type(filename) - self._check_extension(filename) - - self.infile = filename - - reader = vtk.vtkSTLReader() - reader.SetFileName(self.infile) - reader.Update() - data = reader.GetOutput() - - n_points = data.GetNumberOfPoints() - mesh_points = np.zeros([n_points, 3]) - - for i in range(n_points): - mesh_points[i][0], mesh_points[i][1], mesh_points[i][ - 2 - ] = data.GetPoint(i) - - return mesh_points - - def write(self, mesh_points, filename, write_bin=False): - """ - Writes a stl file, called filename, copying all the lines from - self.filename but the coordinates. mesh_points is a matrix that contains - the new coordinates to write in the stl file. - - :param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix - containing the coordinates of the points of the mesh. - :param string filename: name of the output file. - :param boolean write_bin: flag to write in the binary format. Default is - False. - """ - self._check_filename_type(filename) - self._check_extension(filename) - self._check_infile_instantiation() - - self.outfile = filename - - reader = vtk.vtkSTLReader() - reader.SetFileName(self.infile) - reader.Update() - data = reader.GetOutput() - - points = vtk.vtkPoints() - - for i in range(data.GetNumberOfPoints()): - points.InsertNextPoint(mesh_points[i, :]) - - data.SetPoints(points) - - writer = vtk.vtkSTLWriter() - writer.SetFileName(self.outfile) - - if vtk.VTK_MAJOR_VERSION <= 5: - writer.SetInput(data) - else: - writer.SetInputData(data) - - if write_bin: - writer.SetFileTypeToBinary() - else: - writer.SetFileTypeToASCII() - - writer.Write() - - def plot(self, plot_file=None, save_fig=False): - """ - Method to plot an stl file. If `plot_file` is not given it plots - `self.infile`. - - :param string plot_file: the stl filename you want to plot. - :param bool save_fig: a flag to save the figure in png or not. If True - the plot is not shown. The default value is False. - - :return: figure: matlplotlib structure for the figure of the chosen - geometry - :rtype: matplotlib.pyplot.figure - """ - if plot_file is None: - plot_file = self.infile - else: - self._check_filename_type(plot_file) - - # Read the source file. - reader = vtk.vtkSTLReader() - reader.SetFileName(plot_file) - reader.Update() - - data = reader.GetOutput() - points = data.GetPoints() - ncells = data.GetNumberOfCells() - - # for each cell it contains the indeces of the points that define the cell - figure = plt.figure() - axes = a3.Axes3D(figure) - vtx = np.zeros((ncells, 3, 3)) - for i in range(0, ncells): - for j in range(0, 3): - cell = data.GetCell(i).GetPointId(j) - vtx[i][j][0], vtx[i][j][1], vtx[i][j][2] = points.GetPoint( - int(cell) - ) - tri = a3.art3d.Poly3DCollection([vtx[i]]) - tri.set_color('b') - tri.set_edgecolor('k') - axes.add_collection3d(tri) - - ## Get the limits of the axis and center the geometry - max_dim = np.array([np.max(vtx[:,:,0]), \ - np.max(vtx[:,:,1]), \ - np.max(vtx[:,:,2])]) - min_dim = np.array([np.min(vtx[:,:,0]), \ - np.min(vtx[:,:,1]), \ - np.min(vtx[:,:,2])]) - - max_lenght = np.max(max_dim - min_dim) - axes.set_xlim( - -.6 * max_lenght + (max_dim[0] + min_dim[0]) / 2, - .6 * max_lenght + (max_dim[0] + min_dim[0]) / 2 - ) - axes.set_ylim( - -.6 * max_lenght + (max_dim[1] + min_dim[1]) / 2, - .6 * max_lenght + (max_dim[1] + min_dim[1]) / 2 - ) - axes.set_zlim( - -.6 * max_lenght + (max_dim[2] + min_dim[2]) / 2, - .6 * max_lenght + (max_dim[2] + min_dim[2]) / 2 - ) - - # Show the plot to the screen - if not save_fig: - plt.show() - else: - figure.savefig(plot_file.split('.')[0] + '.png') - - return figure - - def show(self, show_file=None): - """ - Method to show a vtk file. If `show_file` is not given it shows - `self.infile`. - - :param string show_file: the vtk filename you want to show. - """ - if show_file is None: - show_file = self.infile - else: - self._check_filename_type(show_file) - - # Read the source file. - reader = vtk.vtkSTLReader() - reader.SetFileName(show_file) - reader.Update() # Needed because of GetScalarRange - output = reader.GetOutput() - scalar_range = output.GetScalarRange() - - # Create the mapper that corresponds the objects of the vtk file - # into graphics elements - mapper = vtk.vtkDataSetMapper() - if vtk.VTK_MAJOR_VERSION <= 5: - mapper.SetInput(output) - else: - mapper.SetInputData(output) - mapper.SetScalarRange(scalar_range) - - # Create the Actor - actor = vtk.vtkActor() - actor.SetMapper(mapper) - - # Create the Renderer - renderer = vtk.vtkRenderer() - renderer.AddActor(actor) - # Set background color (white is 1, 1, 1) - renderer.SetBackground(20, 20, 20) - - # Create the RendererWindow - renderer_window = vtk.vtkRenderWindow() - renderer_window.AddRenderer(renderer) - - # Create the RendererWindowInteractor and display the vtk_file - interactor = vtk.vtkRenderWindowInteractor() - interactor.SetRenderWindow(renderer_window) - interactor.Initialize() - interactor.Start() + """ + STereoLithography file handler class + + :cvar string infile: name of the input file to be processed. + :cvar string outfile: name of the output file where to write in. + :cvar list extensions: extensions of the input/output files. It is equal to + ['.stl']. + """ + + def __init__(self): + super(StlHandler, self).__init__() + self.extensions = ['.stl'] + + def parse(self, filename): + """ + Method to parse the `filename`. It returns a matrix with all the + coordinates. + + :param string filename: name of the input file. + + :return: mesh_points: it is a `n_points`-by-3 matrix containing the + coordinates of the points of the mesh + :rtype: numpy.ndarray + """ + self._check_filename_type(filename) + self._check_extension(filename) + + self.infile = filename + + reader = vtk.vtkSTLReader() + reader.SetFileName(self.infile) + reader.Update() + data = reader.GetOutput() + + n_points = data.GetNumberOfPoints() + mesh_points = np.zeros([n_points, 3]) + + for i in range(n_points): + mesh_points[i][0], mesh_points[i][1], mesh_points[i][ + 2] = data.GetPoint(i) + + return mesh_points + + def write(self, mesh_points, filename, write_bin=False): + """ + Writes a stl file, called filename, copying all the lines from + self.filename but the coordinates. mesh_points is a matrix that contains + the new coordinates to write in the stl file. + + :param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix + containing the coordinates of the points of the mesh. + :param string filename: name of the output file. + :param boolean write_bin: flag to write in the binary format. Default is + False. + """ + self._check_filename_type(filename) + self._check_extension(filename) + self._check_infile_instantiation() + + self.outfile = filename + + reader = vtk.vtkSTLReader() + reader.SetFileName(self.infile) + reader.Update() + data = reader.GetOutput() + + points = vtk.vtkPoints() + + for i in range(data.GetNumberOfPoints()): + points.InsertNextPoint(mesh_points[i, :]) + + data.SetPoints(points) + + writer = vtk.vtkSTLWriter() + writer.SetFileName(self.outfile) + + if vtk.VTK_MAJOR_VERSION <= 5: + writer.SetInput(data) + else: + writer.SetInputData(data) + + if write_bin: + writer.SetFileTypeToBinary() + else: + writer.SetFileTypeToASCII() + + writer.Write() + + def plot(self, plot_file=None, save_fig=False): + """ + Method to plot an stl file. If `plot_file` is not given it plots + `self.infile`. + + :param string plot_file: the stl filename you want to plot. + :param bool save_fig: a flag to save the figure in png or not. If True + the plot is not shown. The default value is False. + + :return: figure: matlplotlib structure for the figure of the chosen + geometry + :rtype: matplotlib.pyplot.figure + """ + if plot_file is None: + plot_file = self.infile + else: + self._check_filename_type(plot_file) + + # Read the source file. + reader = vtk.vtkSTLReader() + reader.SetFileName(plot_file) + reader.Update() + + data = reader.GetOutput() + points = data.GetPoints() + ncells = data.GetNumberOfCells() + + # for each cell it contains the indeces of the points that define the cell + figure = plt.figure() + axes = a3.Axes3D(figure) + vtx = np.zeros((ncells, 3, 3)) + for i in range(0, ncells): + for j in range(0, 3): + cell = data.GetCell(i).GetPointId(j) + vtx[i][j][0], vtx[i][j][1], vtx[i][j][2] = points.GetPoint( + int(cell)) + tri = a3.art3d.Poly3DCollection([vtx[i]]) + tri.set_color('b') + tri.set_edgecolor('k') + axes.add_collection3d(tri) + + ## Get the limits of the axis and center the geometry + max_dim = np.array([np.max(vtx[:,:,0]), \ + np.max(vtx[:,:,1]), \ + np.max(vtx[:,:,2])]) + min_dim = np.array([np.min(vtx[:,:,0]), \ + np.min(vtx[:,:,1]), \ + np.min(vtx[:,:,2])]) + + max_lenght = np.max(max_dim - min_dim) + axes.set_xlim(-.6 * max_lenght + (max_dim[0] + min_dim[0]) / 2, + .6 * max_lenght + (max_dim[0] + min_dim[0]) / 2) + axes.set_ylim(-.6 * max_lenght + (max_dim[1] + min_dim[1]) / 2, + .6 * max_lenght + (max_dim[1] + min_dim[1]) / 2) + axes.set_zlim(-.6 * max_lenght + (max_dim[2] + min_dim[2]) / 2, + .6 * max_lenght + (max_dim[2] + min_dim[2]) / 2) + + # Show the plot to the screen + if not save_fig: + plt.show() + else: + figure.savefig(plot_file.split('.')[0] + '.png') + + return figure + + def show(self, show_file=None): + """ + Method to show a vtk file. If `show_file` is not given it shows + `self.infile`. + + :param string show_file: the vtk filename you want to show. + """ + if show_file is None: + show_file = self.infile + else: + self._check_filename_type(show_file) + + # Read the source file. + reader = vtk.vtkSTLReader() + reader.SetFileName(show_file) + reader.Update() # Needed because of GetScalarRange + output = reader.GetOutput() + scalar_range = output.GetScalarRange() + + # Create the mapper that corresponds the objects of the vtk file + # into graphics elements + mapper = vtk.vtkDataSetMapper() + if vtk.VTK_MAJOR_VERSION <= 5: + mapper.SetInput(output) + else: + mapper.SetInputData(output) + mapper.SetScalarRange(scalar_range) + + # Create the Actor + actor = vtk.vtkActor() + actor.SetMapper(mapper) + + # Create the Renderer + renderer = vtk.vtkRenderer() + renderer.AddActor(actor) + # Set background color (white is 1, 1, 1) + renderer.SetBackground(20, 20, 20) + + # Create the RendererWindow + renderer_window = vtk.vtkRenderWindow() + renderer_window.AddRenderer(renderer) + + # Create the RendererWindowInteractor and display the vtk_file + interactor = vtk.vtkRenderWindowInteractor() + interactor.SetRenderWindow(renderer_window) + interactor.Initialize() + interactor.Start() diff --git a/pygem/unvhandler.py b/pygem/unvhandler.py index b7b6efe..6b67b46 100644 --- a/pygem/unvhandler.py +++ b/pygem/unvhandler.py @@ -6,91 +6,90 @@ class UnvHandler(fh.FileHandler): - """ - Universal file handler class + """ + Universal file handler class - :cvar string infile: name of the input file to be processed. - :cvar string outfile: name of the output file where to write in. - :cvar list extensions: extensions of the input/output files. It is equal to ['.unv']. - """ + :cvar string infile: name of the input file to be processed. + :cvar string outfile: name of the output file where to write in. + :cvar list extensions: extensions of the input/output files. It is equal to ['.unv']. + """ - def __init__(self): - super(UnvHandler, self).__init__() - self.extensions = ['.unv'] + def __init__(self): + super(UnvHandler, self).__init__() + self.extensions = ['.unv'] - def parse(self, filename): - """ - Method to parse the file `filename`. It returns a matrix with all the coordinates. - It reads only the section 2411 of the unv files and it assumes there are only triangles. + def parse(self, filename): + """ + Method to parse the file `filename`. It returns a matrix with all the coordinates. + It reads only the section 2411 of the unv files and it assumes there are only triangles. - :param string filename: name of the input file. - - :return: mesh_points: it is a `n_points`-by-3 matrix containing the coordinates of - the points of the mesh. - :rtype: numpy.ndarray - """ - self._check_filename_type(filename) - self._check_extension(filename) + :param string filename: name of the input file. + + :return: mesh_points: it is a `n_points`-by-3 matrix containing the coordinates of + the points of the mesh. + :rtype: numpy.ndarray + """ + self._check_filename_type(filename) + self._check_extension(filename) - self.infile = filename + self.infile = filename - index = -9 - mesh_points = [] - with open(self.infile, 'r') as input_file: - for num, line in enumerate(input_file): - if line.startswith(' 2411'): - index = num - if num == index + 2: - if line.startswith(' -1'): - break - else: - line = line.replace('D', 'E') - l = [] - for t in line.split(): - try: - l.append(float(t)) - except ValueError: - pass - mesh_points.append(l) - index = num - mesh_points = np.array(mesh_points) + index = -9 + mesh_points = [] + with open(self.infile, 'r') as input_file: + for num, line in enumerate(input_file): + if line.startswith(' 2411'): + index = num + if num == index + 2: + if line.startswith(' -1'): + break + else: + line = line.replace('D', 'E') + l = [] + for t in line.split(): + try: + l.append(float(t)) + except ValueError: + pass + mesh_points.append(l) + index = num + mesh_points = np.array(mesh_points) - return mesh_points + return mesh_points + def write(self, mesh_points, filename): + """ + Writes a unv file, called filename, copying all the lines from self.filename but + the coordinates. mesh_points is a matrix that contains the new coordinates to + write in the unv file. - def write(self, mesh_points, filename): - """ - Writes a unv file, called filename, copying all the lines from self.filename but - the coordinates. mesh_points is a matrix that contains the new coordinates to - write in the unv file. + :param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix containing + the coordinates of the points of the mesh + :param string filename: name of the output file. + """ + self._check_filename_type(filename) + self._check_extension(filename) + self._check_infile_instantiation() - :param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix containing - the coordinates of the points of the mesh - :param string filename: name of the output file. - """ - self._check_filename_type(filename) - self._check_extension(filename) - self._check_infile_instantiation() - - self.outfile = filename - - index = -9 - i = 0 - with open(self.outfile, 'w') as output_file: - with open(self.infile, 'r') as input_file: - for num, line in enumerate(input_file): - if line.startswith(' 2411'): - index = num - if num == index + 2: - if line.startswith(' -1'): - index = -9 - output_file.write(line) - else: - for j in range(0, 3): - output_file.write(3*' ' + '{:.16E}'.format(mesh_points[i][j])) - output_file.write('\n') - i += 1 - index = num - else: - output_file.write(line) + self.outfile = filename + index = -9 + i = 0 + with open(self.outfile, 'w') as output_file: + with open(self.infile, 'r') as input_file: + for num, line in enumerate(input_file): + if line.startswith(' 2411'): + index = num + if num == index + 2: + if line.startswith(' -1'): + index = -9 + output_file.write(line) + else: + for j in range(0, 3): + output_file.write(3 * ' ' + '{:.16E}'.format( + mesh_points[i][j])) + output_file.write('\n') + i += 1 + index = num + else: + output_file.write(line) diff --git a/pygem/utils.py b/pygem/utils.py index 92e721f..a5ce04d 100644 --- a/pygem/utils.py +++ b/pygem/utils.py @@ -7,188 +7,183 @@ def write_bounding_box(parameters, outfile, write_deformed=True): - """ - Method that writes a vtk file containing the FFD lattice. This method allows - to visualize where the FFD control points are located before the geometrical - morphing. If the `write_deformed` flag is set to True the method writes out - the deformed lattice, otherwise it writes one the original undeformed - lattice. - - :param FFDParameters parameters: parameters of the Free Form Deformation. - :param string outfile: name of the output file. - :param bool write_deformed: flag to write the original or modified FFD - control lattice. The default is set to True. - - :Example: - - >>> import pygem.utils as ut - >>> import pygem.params as pars - >>> import numpy as np - - >>> params = pars.FFDParameters() - >>> params.read_parameters(filename='tests/test_datasets/parameters_test_ffd_sphere.prm') - >>> ut.write_bounding_box(params, 'tests/test_datasets/box_test_sphere.vtk') - """ - aux_x = np.linspace( - 0, parameters.lenght_box[0], parameters.n_control_points[0] - ) - aux_y = np.linspace( - 0, parameters.lenght_box[1], parameters.n_control_points[1] - ) - aux_z = np.linspace( - 0, parameters.lenght_box[2], parameters.n_control_points[2] - ) - lattice_y_coords, lattice_x_coords, lattice_z_coords = np.meshgrid( - aux_y, aux_x, aux_z - ) - - if write_deformed: - box_points = np.array([ \ - lattice_x_coords.ravel() + parameters.array_mu_x.ravel() * parameters.lenght_box[0], \ - lattice_y_coords.ravel() + parameters.array_mu_y.ravel() * parameters.lenght_box[1], \ - lattice_z_coords.ravel() + parameters.array_mu_z.ravel() * parameters.lenght_box[2]]) - else: - box_points = np.array([lattice_x_coords.ravel(), lattice_y_coords.ravel(), \ - lattice_z_coords.ravel()]) - - n_rows = box_points.shape[1] - - box_points = np.dot(parameters.rotation_matrix, box_points) + \ - np.transpose(np.tile(parameters.origin_box, (n_rows, 1))) - - # step necessary to set the correct order to the box points for vtkStructuredGrid: - # Data in vtkStructuredGrid are ordered with x increasing fastest, then y, then z - dims = lattice_y_coords.shape - aux_xx = box_points[0, :].reshape(dims).ravel(order='f') - aux_yy = box_points[1, :].reshape(dims).ravel(order='f') - aux_zz = box_points[2, :].reshape(dims).ravel(order='f') - reordered_box_points = np.array((aux_xx, aux_yy, aux_zz)) - - _write_vtk_box(reordered_box_points, outfile, parameters.n_control_points) + """ + Method that writes a vtk file containing the FFD lattice. This method allows + to visualize where the FFD control points are located before the geometrical + morphing. If the `write_deformed` flag is set to True the method writes out + the deformed lattice, otherwise it writes one the original undeformed + lattice. + + :param FFDParameters parameters: parameters of the Free Form Deformation. + :param string outfile: name of the output file. + :param bool write_deformed: flag to write the original or modified FFD + control lattice. The default is set to True. + + :Example: + + >>> import pygem.utils as ut + >>> import pygem.params as pars + >>> import numpy as np + + >>> params = pars.FFDParameters() + >>> params.read_parameters(filename='tests/test_datasets/parameters_test_ffd_sphere.prm') + >>> ut.write_bounding_box(params, 'tests/test_datasets/box_test_sphere.vtk') + """ + aux_x = np.linspace(0, parameters.lenght_box[0], + parameters.n_control_points[0]) + aux_y = np.linspace(0, parameters.lenght_box[1], + parameters.n_control_points[1]) + aux_z = np.linspace(0, parameters.lenght_box[2], + parameters.n_control_points[2]) + lattice_y_coords, lattice_x_coords, lattice_z_coords = np.meshgrid( + aux_y, aux_x, aux_z) + + if write_deformed: + box_points = np.array([ \ + lattice_x_coords.ravel() + parameters.array_mu_x.ravel() * parameters.lenght_box[0], \ + lattice_y_coords.ravel() + parameters.array_mu_y.ravel() * parameters.lenght_box[1], \ + lattice_z_coords.ravel() + parameters.array_mu_z.ravel() * parameters.lenght_box[2]]) + else: + box_points = np.array([lattice_x_coords.ravel(), lattice_y_coords.ravel(), \ + lattice_z_coords.ravel()]) + + n_rows = box_points.shape[1] + + box_points = np.dot(parameters.rotation_matrix, box_points) + \ + np.transpose(np.tile(parameters.origin_box, (n_rows, 1))) + + # step necessary to set the correct order to the box points for vtkStructuredGrid: + # Data in vtkStructuredGrid are ordered with x increasing fastest, then y, then z + dims = lattice_y_coords.shape + aux_xx = box_points[0, :].reshape(dims).ravel(order='f') + aux_yy = box_points[1, :].reshape(dims).ravel(order='f') + aux_zz = box_points[2, :].reshape(dims).ravel(order='f') + reordered_box_points = np.array((aux_xx, aux_yy, aux_zz)) + + _write_vtk_box(reordered_box_points, outfile, parameters.n_control_points) def _write_vtk_box(box_points, filename, dimensions): - """ - Private method that writes a vtk file containing FFD control points. - - :param numpy.ndarray box_points: coordinates of the FFD control points. - :param string filename: name of the output file. - :param list dimensions: dimension of the lattice in (x, y, z) directions. - - .. warning:: - If you want to visualize in paraview the inner points, - you have to slice the lattice because paraview does not visualize them automatically - even in the wireframe visualization. - """ - # setup points and vertices - points = vtk.vtkPoints() - - for index in range(0, box_points.shape[1]): - points.InsertNextPoint( - box_points[0, index], box_points[1, index], box_points[2, index] - ) - - grid = vtk.vtkStructuredGrid() - - grid.SetPoints(points) - grid.SetDimensions(dimensions) - grid.Modified() - - writer = vtk.vtkStructuredGridWriter() - writer.SetFileName(filename) - - if vtk.VTK_MAJOR_VERSION <= 5: - grid.Update() - writer.SetInput(grid) - else: - writer.SetInputData(grid) - - writer.Write() + """ + Private method that writes a vtk file containing FFD control points. + + :param numpy.ndarray box_points: coordinates of the FFD control points. + :param string filename: name of the output file. + :param list dimensions: dimension of the lattice in (x, y, z) directions. + + .. warning:: + If you want to visualize in paraview the inner points, + you have to slice the lattice because paraview does not visualize them automatically + even in the wireframe visualization. + """ + # setup points and vertices + points = vtk.vtkPoints() + + for index in range(0, box_points.shape[1]): + points.InsertNextPoint(box_points[0, index], box_points[1, index], + box_points[2, index]) + + grid = vtk.vtkStructuredGrid() + + grid.SetPoints(points) + grid.SetDimensions(dimensions) + grid.Modified() + + writer = vtk.vtkStructuredGridWriter() + writer.SetFileName(filename) + + if vtk.VTK_MAJOR_VERSION <= 5: + grid.Update() + writer.SetInput(grid) + else: + writer.SetInputData(grid) + + writer.Write() def plot_rbf_control_points(parameters, save_fig=False): - """ - Method to plot the control points of a RBFParameters class. It is possible to save the - resulting figure. - - :param RBFParameters parameters: parameters of the Radial Basis Functions interpolation. - :param bool save_fig: a flag to save the figure in png or not. If True the - plot is not shown and the figure is saved with the name 'RBF_control_points.png'. - The default value is False. - """ - fig = plt.figure(1) - axes = fig.add_subplot(111, projection='3d') - orig = axes.scatter(parameters.original_control_points[:, 0], \ - parameters.original_control_points[:, 1], \ - parameters.original_control_points[:, 2], c='blue', marker='o') - defor = axes.scatter(parameters.deformed_control_points[:, 0], \ - parameters.deformed_control_points[:, 1], \ - parameters.deformed_control_points[:, 2], c='red', marker='x') - - axes.set_xlabel('X axis') - axes.set_ylabel('Y axis') - axes.set_zlabel('Z axis') - - plt.legend((orig, defor), \ - ('Original', 'Deformed'), \ - scatterpoints=1, \ - loc='lower left', \ - ncol=2, \ - fontsize=10) - - # Show the plot to the screen - if not save_fig: - plt.show() - else: - fig.savefig('RBF_control_points.png') + """ + Method to plot the control points of a RBFParameters class. It is possible to save the + resulting figure. + + :param RBFParameters parameters: parameters of the Radial Basis Functions interpolation. + :param bool save_fig: a flag to save the figure in png or not. If True the + plot is not shown and the figure is saved with the name 'RBF_control_points.png'. + The default value is False. + """ + fig = plt.figure(1) + axes = fig.add_subplot(111, projection='3d') + orig = axes.scatter(parameters.original_control_points[:, 0], \ + parameters.original_control_points[:, 1], \ + parameters.original_control_points[:, 2], c='blue', marker='o') + defor = axes.scatter(parameters.deformed_control_points[:, 0], \ + parameters.deformed_control_points[:, 1], \ + parameters.deformed_control_points[:, 2], c='red', marker='x') + + axes.set_xlabel('X axis') + axes.set_ylabel('Y axis') + axes.set_zlabel('Z axis') + + plt.legend((orig, defor), \ + ('Original', 'Deformed'), \ + scatterpoints=1, \ + loc='lower left', \ + ncol=2, \ + fontsize=10) + + # Show the plot to the screen + if not save_fig: + plt.show() + else: + fig.savefig('RBF_control_points.png') def write_points_in_vtp(points, outfile='points.vtp', color=None): - """ - Method that writes a vtp file containing the given points. It can be used for any set of - 3D points. Useful to visualize control points together with mesh points in the same window. - - :param numpy.ndarray points: coordinates of the points. The shape has to be (n_points, 3). - :param string outfile: name of the output file. The extension has to be .vtp. Default is 'points.vtp'. - :param tuple color: tuple defining the RGB color to assign to all the points. Default is - blue: (0, 0, 255). - - :Example: - - >>> import pygem.utils as ut - >>> import numpy as np - - >>> ctrl_points = np.arange(9).reshape(3, 3) - >>> ut.write_points_in_vtp(ctrl_points, 'example_points.vtp', color=(255, 0, 0)) - """ - if color is None: - color = (0, 0, 255) - # setup points and vertices - Points = vtk.vtkPoints() - Vertices = vtk.vtkCellArray() - - Colors = vtk.vtkUnsignedCharArray() - Colors.SetNumberOfComponents(3) - Colors.SetName("Colors") - - for i in range(points.shape[0]): - ind = Points.InsertNextPoint(points[i][0], points[i][1], points[i][2]) - Vertices.InsertNextCell(1) - Vertices.InsertCellPoint(ind) - Colors.InsertNextTuple3(color[0], color[1], color[2]) - - polydata = vtk.vtkPolyData() - polydata.SetPoints(Points) - polydata.SetVerts(Vertices) - polydata.GetPointData().SetScalars(Colors) - polydata.Modified() - if vtk.VTK_MAJOR_VERSION <= 5: - polydata.Update() - - writer = vtk.vtkXMLPolyDataWriter() - writer.SetFileName(outfile) - if vtk.VTK_MAJOR_VERSION <= 5: - writer.SetInput(polydata) - else: - writer.SetInputData(polydata) - writer.Write() + """ + Method that writes a vtp file containing the given points. It can be used for any set of + 3D points. Useful to visualize control points together with mesh points in the same window. + + :param numpy.ndarray points: coordinates of the points. The shape has to be (n_points, 3). + :param string outfile: name of the output file. The extension has to be .vtp. Default is 'points.vtp'. + :param tuple color: tuple defining the RGB color to assign to all the points. Default is + blue: (0, 0, 255). + + :Example: + + >>> import pygem.utils as ut + >>> import numpy as np + + >>> ctrl_points = np.arange(9).reshape(3, 3) + >>> ut.write_points_in_vtp(ctrl_points, 'example_points.vtp', color=(255, 0, 0)) + """ + if color is None: + color = (0, 0, 255) + # setup points and vertices + Points = vtk.vtkPoints() + Vertices = vtk.vtkCellArray() + + Colors = vtk.vtkUnsignedCharArray() + Colors.SetNumberOfComponents(3) + Colors.SetName("Colors") + + for i in range(points.shape[0]): + ind = Points.InsertNextPoint(points[i][0], points[i][1], points[i][2]) + Vertices.InsertNextCell(1) + Vertices.InsertCellPoint(ind) + Colors.InsertNextTuple3(color[0], color[1], color[2]) + + polydata = vtk.vtkPolyData() + polydata.SetPoints(Points) + polydata.SetVerts(Vertices) + polydata.GetPointData().SetScalars(Colors) + polydata.Modified() + if vtk.VTK_MAJOR_VERSION <= 5: + polydata.Update() + + writer = vtk.vtkXMLPolyDataWriter() + writer.SetFileName(outfile) + if vtk.VTK_MAJOR_VERSION <= 5: + writer.SetInput(polydata) + else: + writer.SetInputData(polydata) + writer.Write() diff --git a/pygem/vtkhandler.py b/pygem/vtkhandler.py index a2da73b..3badccd 100644 --- a/pygem/vtkhandler.py +++ b/pygem/vtkhandler.py @@ -9,207 +9,199 @@ class VtkHandler(fh.FileHandler): - """ - Vtk file handler class - - :cvar string infile: name of the input file to be processed. - :cvar string outfile: name of the output file where to write in. - :cvar list extensions: extensions of the input/output files. It is equal to ['.vtk']. - """ - - def __init__(self): - super(VtkHandler, self).__init__() - self.extensions = ['.vtk'] - - def parse(self, filename): - """ - Method to parse the file `filename`. It returns a matrix with all the coordinates. - - :param string filename: name of the input file. - - :return: mesh_points: it is a `n_points`-by-3 matrix containing the coordinates of - the points of the mesh - :rtype: numpy.ndarray - - .. todo:: - - - specify when it works - """ - self._check_filename_type(filename) - self._check_extension(filename) - - self.infile = filename - - reader = vtk.vtkDataSetReader() - reader.SetFileName(self.infile) - reader.ReadAllVectorsOn() - reader.ReadAllScalarsOn() - reader.Update() - data = reader.GetOutput() - - n_points = data.GetNumberOfPoints() - mesh_points = np.zeros([n_points, 3]) - - for i in range(n_points): - mesh_points[i][0], mesh_points[i][1], mesh_points[i][ - 2 - ] = data.GetPoint(i) - - return mesh_points - - def write(self, mesh_points, filename): - """ - Writes a vtk file, called filename, copying all the structures from self.filename but - the coordinates. mesh_points is a matrix that contains the new coordinates to - write in the vtk file. - - :param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix containing - the coordinates of the points of the mesh - :param string filename: name of the output file. - """ - self._check_filename_type(filename) - self._check_extension(filename) - self._check_infile_instantiation() - - self.outfile = filename - - reader = vtk.vtkDataSetReader() - reader.SetFileName(self.infile) - reader.ReadAllVectorsOn() - reader.ReadAllScalarsOn() - reader.Update() - data = reader.GetOutput() - - points = vtk.vtkPoints() - - for i in range(data.GetNumberOfPoints()): - points.InsertNextPoint(mesh_points[i, :]) - - data.SetPoints(points) - - writer = vtk.vtkDataSetWriter() - writer.SetFileName(self.outfile) - - if vtk.VTK_MAJOR_VERSION <= 5: - writer.SetInput(data) - else: - writer.SetInputData(data) - - writer.Write() - - def plot(self, plot_file=None, save_fig=False): - """ - Method to plot a vtk file. If `plot_file` is not given it plots `self.infile`. - - :param string plot_file: the vtk filename you want to plot. - :param bool save_fig: a flag to save the figure in png or not. If True the - plot is not shown. - - :return: figure: matlplotlib structure for the figure of the chosen geometry - :rtype: matplotlib.pyplot.figure - """ - if plot_file is None: - plot_file = self.infile - else: - self._check_filename_type(plot_file) - - # Read the source file. - reader = vtk.vtkDataSetReader() - reader.SetFileName(plot_file) - reader.Update() - - data = reader.GetOutput() - points = data.GetPoints() - ncells = data.GetNumberOfCells() - - # for each cell it contains the indeces of the points that define the cell - figure = plt.figure() - axes = a3.Axes3D(figure) - vtx = np.zeros((ncells, 3, 3)) - for i in range(0, ncells): - for j in range(0, 3): - cell = data.GetCell(i).GetPointId(j) - vtx[i][j][0], vtx[i][j][1], vtx[i][j][2] = points.GetPoint( - int(cell) - ) - tri = a3.art3d.Poly3DCollection([vtx[i]]) - tri.set_color('b') - tri.set_edgecolor('k') - axes.add_collection3d(tri) - - ## Get the limits of the axis and center the geometry - max_dim = np.array([np.max(vtx[:,:,0]), \ - np.max(vtx[:,:,1]), \ - np.max(vtx[:,:,2])]) - min_dim = np.array([np.min(vtx[:,:,0]), \ - np.min(vtx[:,:,1]), \ - np.min(vtx[:,:,2])]) - - max_lenght = np.max(max_dim - min_dim) - axes.set_xlim( - -.6 * max_lenght + (max_dim[0] + min_dim[0]) / 2, - .6 * max_lenght + (max_dim[0] + min_dim[0]) / 2 - ) - axes.set_ylim( - -.6 * max_lenght + (max_dim[1] + min_dim[1]) / 2, - .6 * max_lenght + (max_dim[1] + min_dim[1]) / 2 - ) - axes.set_zlim( - -.6 * max_lenght + (max_dim[2] + min_dim[2]) / 2, - .6 * max_lenght + (max_dim[2] + min_dim[2]) / 2 - ) - - # Show the plot to the screen - if not save_fig: - plt.show() - else: - figure.savefig(plot_file.split('.')[0] + '.png') - - return figure - - def show(self, show_file=None): - """ - Method to show a vtk file. If `show_file` is not given it shows `self.infile`. - - :param string show_file: the vtk filename you want to show. - """ - if show_file is None: - show_file = self.infile - else: - self._check_filename_type(show_file) - - # Read the source file. - reader = vtk.vtkUnstructuredGridReader() - reader.SetFileName(show_file) - reader.Update() # Needed because of GetScalarRange - output = reader.GetOutput() - scalar_range = output.GetScalarRange() - - # Create the mapper that corresponds the objects of the vtk file - # into graphics elements - mapper = vtk.vtkDataSetMapper() - if vtk.VTK_MAJOR_VERSION <= 5: - mapper.SetInput(output) - else: - mapper.SetInputData(output) - mapper.SetScalarRange(scalar_range) - - # Create the Actor - actor = vtk.vtkActor() - actor.SetMapper(mapper) - - # Create the Renderer - renderer = vtk.vtkRenderer() - renderer.AddActor(actor) - # Set background color (white is 1, 1, 1) - renderer.SetBackground(20, 20, 20) - - # Create the RendererWindow - renderer_window = vtk.vtkRenderWindow() - renderer_window.AddRenderer(renderer) - - # Create the RendererWindowInteractor and display the vtk_file - interactor = vtk.vtkRenderWindowInteractor() - interactor.SetRenderWindow(renderer_window) - interactor.Initialize() - interactor.Start() + """ + Vtk file handler class + + :cvar string infile: name of the input file to be processed. + :cvar string outfile: name of the output file where to write in. + :cvar list extensions: extensions of the input/output files. It is equal to ['.vtk']. + """ + + def __init__(self): + super(VtkHandler, self).__init__() + self.extensions = ['.vtk'] + + def parse(self, filename): + """ + Method to parse the file `filename`. It returns a matrix with all the coordinates. + + :param string filename: name of the input file. + + :return: mesh_points: it is a `n_points`-by-3 matrix containing the coordinates of + the points of the mesh + :rtype: numpy.ndarray + + .. todo:: + + - specify when it works + """ + self._check_filename_type(filename) + self._check_extension(filename) + + self.infile = filename + + reader = vtk.vtkDataSetReader() + reader.SetFileName(self.infile) + reader.ReadAllVectorsOn() + reader.ReadAllScalarsOn() + reader.Update() + data = reader.GetOutput() + + n_points = data.GetNumberOfPoints() + mesh_points = np.zeros([n_points, 3]) + + for i in range(n_points): + mesh_points[i][0], mesh_points[i][1], mesh_points[i][ + 2] = data.GetPoint(i) + + return mesh_points + + def write(self, mesh_points, filename): + """ + Writes a vtk file, called filename, copying all the structures from self.filename but + the coordinates. mesh_points is a matrix that contains the new coordinates to + write in the vtk file. + + :param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix containing + the coordinates of the points of the mesh + :param string filename: name of the output file. + """ + self._check_filename_type(filename) + self._check_extension(filename) + self._check_infile_instantiation() + + self.outfile = filename + + reader = vtk.vtkDataSetReader() + reader.SetFileName(self.infile) + reader.ReadAllVectorsOn() + reader.ReadAllScalarsOn() + reader.Update() + data = reader.GetOutput() + + points = vtk.vtkPoints() + + for i in range(data.GetNumberOfPoints()): + points.InsertNextPoint(mesh_points[i, :]) + + data.SetPoints(points) + + writer = vtk.vtkDataSetWriter() + writer.SetFileName(self.outfile) + + if vtk.VTK_MAJOR_VERSION <= 5: + writer.SetInput(data) + else: + writer.SetInputData(data) + + writer.Write() + + def plot(self, plot_file=None, save_fig=False): + """ + Method to plot a vtk file. If `plot_file` is not given it plots `self.infile`. + + :param string plot_file: the vtk filename you want to plot. + :param bool save_fig: a flag to save the figure in png or not. If True the + plot is not shown. + + :return: figure: matlplotlib structure for the figure of the chosen geometry + :rtype: matplotlib.pyplot.figure + """ + if plot_file is None: + plot_file = self.infile + else: + self._check_filename_type(plot_file) + + # Read the source file. + reader = vtk.vtkDataSetReader() + reader.SetFileName(plot_file) + reader.Update() + + data = reader.GetOutput() + points = data.GetPoints() + ncells = data.GetNumberOfCells() + + # for each cell it contains the indeces of the points that define the cell + figure = plt.figure() + axes = a3.Axes3D(figure) + vtx = np.zeros((ncells, 3, 3)) + for i in range(0, ncells): + for j in range(0, 3): + cell = data.GetCell(i).GetPointId(j) + vtx[i][j][0], vtx[i][j][1], vtx[i][j][2] = points.GetPoint( + int(cell)) + tri = a3.art3d.Poly3DCollection([vtx[i]]) + tri.set_color('b') + tri.set_edgecolor('k') + axes.add_collection3d(tri) + + ## Get the limits of the axis and center the geometry + max_dim = np.array([np.max(vtx[:,:,0]), \ + np.max(vtx[:,:,1]), \ + np.max(vtx[:,:,2])]) + min_dim = np.array([np.min(vtx[:,:,0]), \ + np.min(vtx[:,:,1]), \ + np.min(vtx[:,:,2])]) + + max_lenght = np.max(max_dim - min_dim) + axes.set_xlim(-.6 * max_lenght + (max_dim[0] + min_dim[0]) / 2, + .6 * max_lenght + (max_dim[0] + min_dim[0]) / 2) + axes.set_ylim(-.6 * max_lenght + (max_dim[1] + min_dim[1]) / 2, + .6 * max_lenght + (max_dim[1] + min_dim[1]) / 2) + axes.set_zlim(-.6 * max_lenght + (max_dim[2] + min_dim[2]) / 2, + .6 * max_lenght + (max_dim[2] + min_dim[2]) / 2) + + # Show the plot to the screen + if not save_fig: + plt.show() + else: + figure.savefig(plot_file.split('.')[0] + '.png') + + return figure + + def show(self, show_file=None): + """ + Method to show a vtk file. If `show_file` is not given it shows `self.infile`. + + :param string show_file: the vtk filename you want to show. + """ + if show_file is None: + show_file = self.infile + else: + self._check_filename_type(show_file) + + # Read the source file. + reader = vtk.vtkUnstructuredGridReader() + reader.SetFileName(show_file) + reader.Update() # Needed because of GetScalarRange + output = reader.GetOutput() + scalar_range = output.GetScalarRange() + + # Create the mapper that corresponds the objects of the vtk file + # into graphics elements + mapper = vtk.vtkDataSetMapper() + if vtk.VTK_MAJOR_VERSION <= 5: + mapper.SetInput(output) + else: + mapper.SetInputData(output) + mapper.SetScalarRange(scalar_range) + + # Create the Actor + actor = vtk.vtkActor() + actor.SetMapper(mapper) + + # Create the Renderer + renderer = vtk.vtkRenderer() + renderer.AddActor(actor) + # Set background color (white is 1, 1, 1) + renderer.SetBackground(20, 20, 20) + + # Create the RendererWindow + renderer_window = vtk.vtkRenderWindow() + renderer_window.AddRenderer(renderer) + + # Create the RendererWindowInteractor and display the vtk_file + interactor = vtk.vtkRenderWindowInteractor() + interactor.SetRenderWindow(renderer_window) + interactor.Initialize() + interactor.Start() diff --git a/tests/test_affine.py b/tests/test_affine.py index 06b6ea2..4e05d35 100644 --- a/tests/test_affine.py +++ b/tests/test_affine.py @@ -5,163 +5,161 @@ class TestAffine(TestCase): - def test_angles2matrix_rot_default(self): - mat_exact = np.eye(3) - mat_test = at.angles2matrix() - np.testing.assert_array_almost_equal(mat_exact, mat_test) - - def test_angles2matrix_rot_x(self): - rotz = 0 - roty = 0 - rotx = 50 * np.pi / 180 - mat_exact = np.array([ - 1., 0., 0., 0., 0.64278761, -0.76604444, 0., 0.76604444, 0.64278761 - ]).reshape((3, 3)) - - mat_test = at.angles2matrix(rotz, roty, rotx) - np.testing.assert_array_almost_equal(mat_exact, mat_test) - - def test_angles2matrix_rot_y(self): - rotz = 0 - roty = 23 * np.pi / 180 - rotx = 0 - mat_exact = np.array([ - 0.92050485, 0., 0.39073113, 0., 1., 0., -0.39073113, 0., 0.92050485 - ]).reshape((3, 3)) - - mat_test = at.angles2matrix(rotz, roty, rotx) - np.testing.assert_array_almost_equal(mat_exact, mat_test) - - def test_angles2matrix_rot_z(self): - rotz = -57 * np.pi / 180 - roty = 0 - rotx = 0 - mat_exact = np.array([ - 0.54463904, 0.83867057, 0., -0.83867057, 0.54463904, 0., 0., 0., 1. - ]).reshape((3, 3)) - - mat_test = at.angles2matrix(rotz, roty, rotx) - np.testing.assert_array_almost_equal(mat_exact, mat_test) - - def test_angles2matrix_rot_xyz(self): - rotz = 10 * np.pi / 180 - roty = 20 * np.pi / 180 - rotx = 30 * np.pi / 180 - mat_exact = np.array([0.92541658, -0.16317591, 0.34202014, 0.31879578, \ - 0.82317294, -0.46984631, -0.20487413, 0.54383814, 0.81379768]).reshape((3,3)) - - mat_test = at.angles2matrix(rotz, roty, rotx) - np.testing.assert_array_almost_equal(mat_exact, mat_test) - - def test_to_reduced_row_echelon_form_1(self): - matrix = [[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]] - rref_matrix = at.to_reduced_row_echelon_form(matrix) - rref_matrix_exact = [[1.0, 1.0, 1.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] - assert rref_matrix == rref_matrix_exact - - def test_to_reduced_row_echelon_form_2(self): - matrix = [[1., -1., 1.], [-1., 1., -1.], [3., 4., 5.]] - rref_matrix = at.to_reduced_row_echelon_form(matrix) - rref_matrix_exact = [[1.0, 0.0, 1.2857142857142856], - [0.0, 1.0, 0.2857142857142857], [0.0, 0.0, 0.0]] - assert rref_matrix == rref_matrix_exact - - def test_to_reduced_row_echelon_form_3(self): - matrix = [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]] - rref_matrix = at.to_reduced_row_echelon_form(matrix) - rref_matrix_exact = [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]] - assert rref_matrix == rref_matrix_exact - - def test_to_reduced_row_echelon_form_4(self): - matrix = [[0., 0., 0.], [-3., 6., -3.], [0., 0., 0.]] - rref_matrix = at.to_reduced_row_echelon_form(matrix) - rref_matrix_exact = [[1.0, -2.0, 1.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] - assert rref_matrix == rref_matrix_exact - - def test_to_reduced_row_echelon_form_5(self): - matrix = [[0., 0., 0.], [0., 0., 0.], [2., 4., 6.]] - rref_matrix = at.to_reduced_row_echelon_form(matrix) - rref_matrix_exact = [[1.0, 2.0, 3.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] - assert rref_matrix == rref_matrix_exact - - def test_to_reduced_row_echelon_form_6(self): - matrix = [[0., 0., 0.], [0., 0., 0.], [2., 4., 6.], [1., 0., 0.]] - rref_matrix = at.to_reduced_row_echelon_form(matrix) - rref_matrix_exact = [[1.0, 0.0, 0.0], [-0.0, 1.0, 1.5], [0.0, 0.0, 0.0], - [0.0, 0.0, 0.0]] - assert rref_matrix == rref_matrix_exact - - def test_to_reduced_row_echelon_form_7(self): - matrix = [[0., 0., 0., 4], [0., 0., -3., 2], [2., 4., 6., 0]] - rref_matrix = at.to_reduced_row_echelon_form(matrix) - rref_matrix_exact = [[1.0, 2.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], - [0.0, 0.0, 0.0, 1.0]] - assert rref_matrix == rref_matrix_exact - - def test_affine_points_fit_identity_1(self): - p_start = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 0]]) - p_end = p_start - v_test = np.array([1., 2., 3.]) - v_exact = v_test - - transformation = at.affine_points_fit(p_start, p_end) - v_trans = transformation(v_test) - np.testing.assert_array_almost_equal(v_exact, v_trans) - - def test_affine_points_fit_identity_2(self): - p_start = np.array( - [[1, .5, -.3], [0, 2, 4], [-1, 0., -1.5], [1, -4, .5]] - ) - p_end = p_start - v_test = np.array([-1., 2.5, .3]) - v_exact = v_test - - transformation = at.affine_points_fit(p_start, p_end) - v_trans = transformation(v_test) - np.testing.assert_array_almost_equal(v_exact, v_trans) - - def test_affine_points_fit_rotation(self): - p_start = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 0]]) - p_end = np.array([[0, 1, 0], [-1, 0, 0], [0, 0, 1], [0, 0, 0]]) - v_test = np.array([1., 2., 3.]) - v_exact = np.array([-2., 1., 3.]) - - transformation = at.affine_points_fit(p_start, p_end) - v_trans = transformation(v_test) - np.testing.assert_array_almost_equal(v_exact, v_trans) - - def test_affine_points_fit_generic(self): - p_start = np.array( - [[1, .5, -.3], [0, 2, 4], [-1, 0., -1.5], [1, -4, .5]] - ) - p_end = np.array([[0, 1, 0], [-1, 0, 0], [0, 0, 1], [0, 0, 0]]) - v_test = np.array([1., 2., 3.]) - v_exact = np.array([-0.68443497, 0.7249467, -0.34221748]) - - transformation = at.affine_points_fit(p_start, p_end) - v_trans = transformation(v_test) - np.testing.assert_array_almost_equal(v_exact, v_trans) - - def test_affine_points_fit_coplanar(self): - p_start = np.array([[0, 0, 0], [0, 0, 0], [1, 1, 1], [1, 1, 1]]) - p_end = np.array([[0, 1, 0], [-1, 0, 0], [0, 0, 1], [0, 0, 0]]) - with self.assertRaises(RuntimeError): - transformation = at.affine_points_fit(p_start, p_end) - - def test_affine_points_fit_right_points_size(self): - p_start = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 0]]) - p_end = np.array([[0, 1, 0], [-1, 0, 0], [0, 0, 1]]) - with self.assertRaises(RuntimeError): - transformation = at.affine_points_fit(p_start, p_end) - - def test_affine_points_fit_under_determined_system_1(self): - p_start = np.array([[1, 0, 0]]) - p_end = np.array([[0, 1, 0]]) - with self.assertRaises(RuntimeError): - transformation = at.affine_points_fit(p_start, p_end) - - def test_affine_points_fit_under_determined_system_2(self): - p_start = np.array([[1, 0, 0], [0, 1, 0]]) - p_end = np.array([[0, 1, 0], [-1, 0, 0]]) - with self.assertRaises(RuntimeError): - transformation = at.affine_points_fit(p_start, p_end) + def test_angles2matrix_rot_default(self): + mat_exact = np.eye(3) + mat_test = at.angles2matrix() + np.testing.assert_array_almost_equal(mat_exact, mat_test) + + def test_angles2matrix_rot_x(self): + rotz = 0 + roty = 0 + rotx = 50 * np.pi / 180 + mat_exact = np.array([ + 1., 0., 0., 0., 0.64278761, -0.76604444, 0., 0.76604444, 0.64278761 + ]).reshape((3, 3)) + + mat_test = at.angles2matrix(rotz, roty, rotx) + np.testing.assert_array_almost_equal(mat_exact, mat_test) + + def test_angles2matrix_rot_y(self): + rotz = 0 + roty = 23 * np.pi / 180 + rotx = 0 + mat_exact = np.array([ + 0.92050485, 0., 0.39073113, 0., 1., 0., -0.39073113, 0., 0.92050485 + ]).reshape((3, 3)) + + mat_test = at.angles2matrix(rotz, roty, rotx) + np.testing.assert_array_almost_equal(mat_exact, mat_test) + + def test_angles2matrix_rot_z(self): + rotz = -57 * np.pi / 180 + roty = 0 + rotx = 0 + mat_exact = np.array([ + 0.54463904, 0.83867057, 0., -0.83867057, 0.54463904, 0., 0., 0., 1. + ]).reshape((3, 3)) + + mat_test = at.angles2matrix(rotz, roty, rotx) + np.testing.assert_array_almost_equal(mat_exact, mat_test) + + def test_angles2matrix_rot_xyz(self): + rotz = 10 * np.pi / 180 + roty = 20 * np.pi / 180 + rotx = 30 * np.pi / 180 + mat_exact = np.array([0.92541658, -0.16317591, 0.34202014, 0.31879578, \ + 0.82317294, -0.46984631, -0.20487413, 0.54383814, 0.81379768]).reshape((3,3)) + + mat_test = at.angles2matrix(rotz, roty, rotx) + np.testing.assert_array_almost_equal(mat_exact, mat_test) + + def test_to_reduced_row_echelon_form_1(self): + matrix = [[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]] + rref_matrix = at.to_reduced_row_echelon_form(matrix) + rref_matrix_exact = [[1.0, 1.0, 1.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] + assert rref_matrix == rref_matrix_exact + + def test_to_reduced_row_echelon_form_2(self): + matrix = [[1., -1., 1.], [-1., 1., -1.], [3., 4., 5.]] + rref_matrix = at.to_reduced_row_echelon_form(matrix) + rref_matrix_exact = [[1.0, 0.0, 1.2857142857142856], + [0.0, 1.0, 0.2857142857142857], [0.0, 0.0, 0.0]] + assert rref_matrix == rref_matrix_exact + + def test_to_reduced_row_echelon_form_3(self): + matrix = [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]] + rref_matrix = at.to_reduced_row_echelon_form(matrix) + rref_matrix_exact = [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]] + assert rref_matrix == rref_matrix_exact + + def test_to_reduced_row_echelon_form_4(self): + matrix = [[0., 0., 0.], [-3., 6., -3.], [0., 0., 0.]] + rref_matrix = at.to_reduced_row_echelon_form(matrix) + rref_matrix_exact = [[1.0, -2.0, 1.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] + assert rref_matrix == rref_matrix_exact + + def test_to_reduced_row_echelon_form_5(self): + matrix = [[0., 0., 0.], [0., 0., 0.], [2., 4., 6.]] + rref_matrix = at.to_reduced_row_echelon_form(matrix) + rref_matrix_exact = [[1.0, 2.0, 3.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] + assert rref_matrix == rref_matrix_exact + + def test_to_reduced_row_echelon_form_6(self): + matrix = [[0., 0., 0.], [0., 0., 0.], [2., 4., 6.], [1., 0., 0.]] + rref_matrix = at.to_reduced_row_echelon_form(matrix) + rref_matrix_exact = [[1.0, 0.0, 0.0], [-0.0, 1.0, 1.5], [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0]] + assert rref_matrix == rref_matrix_exact + + def test_to_reduced_row_echelon_form_7(self): + matrix = [[0., 0., 0., 4], [0., 0., -3., 2], [2., 4., 6., 0]] + rref_matrix = at.to_reduced_row_echelon_form(matrix) + rref_matrix_exact = [[1.0, 2.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0]] + assert rref_matrix == rref_matrix_exact + + def test_affine_points_fit_identity_1(self): + p_start = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 0]]) + p_end = p_start + v_test = np.array([1., 2., 3.]) + v_exact = v_test + + transformation = at.affine_points_fit(p_start, p_end) + v_trans = transformation(v_test) + np.testing.assert_array_almost_equal(v_exact, v_trans) + + def test_affine_points_fit_identity_2(self): + p_start = np.array([[1, .5, -.3], [0, 2, 4], [-1, 0., -1.5], + [1, -4, .5]]) + p_end = p_start + v_test = np.array([-1., 2.5, .3]) + v_exact = v_test + + transformation = at.affine_points_fit(p_start, p_end) + v_trans = transformation(v_test) + np.testing.assert_array_almost_equal(v_exact, v_trans) + + def test_affine_points_fit_rotation(self): + p_start = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 0]]) + p_end = np.array([[0, 1, 0], [-1, 0, 0], [0, 0, 1], [0, 0, 0]]) + v_test = np.array([1., 2., 3.]) + v_exact = np.array([-2., 1., 3.]) + + transformation = at.affine_points_fit(p_start, p_end) + v_trans = transformation(v_test) + np.testing.assert_array_almost_equal(v_exact, v_trans) + + def test_affine_points_fit_generic(self): + p_start = np.array([[1, .5, -.3], [0, 2, 4], [-1, 0., -1.5], + [1, -4, .5]]) + p_end = np.array([[0, 1, 0], [-1, 0, 0], [0, 0, 1], [0, 0, 0]]) + v_test = np.array([1., 2., 3.]) + v_exact = np.array([-0.68443497, 0.7249467, -0.34221748]) + + transformation = at.affine_points_fit(p_start, p_end) + v_trans = transformation(v_test) + np.testing.assert_array_almost_equal(v_exact, v_trans) + + def test_affine_points_fit_coplanar(self): + p_start = np.array([[0, 0, 0], [0, 0, 0], [1, 1, 1], [1, 1, 1]]) + p_end = np.array([[0, 1, 0], [-1, 0, 0], [0, 0, 1], [0, 0, 0]]) + with self.assertRaises(RuntimeError): + transformation = at.affine_points_fit(p_start, p_end) + + def test_affine_points_fit_right_points_size(self): + p_start = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 0]]) + p_end = np.array([[0, 1, 0], [-1, 0, 0], [0, 0, 1]]) + with self.assertRaises(RuntimeError): + transformation = at.affine_points_fit(p_start, p_end) + + def test_affine_points_fit_under_determined_system_1(self): + p_start = np.array([[1, 0, 0]]) + p_end = np.array([[0, 1, 0]]) + with self.assertRaises(RuntimeError): + transformation = at.affine_points_fit(p_start, p_end) + + def test_affine_points_fit_under_determined_system_2(self): + p_start = np.array([[1, 0, 0], [0, 1, 0]]) + p_end = np.array([[0, 1, 0], [-1, 0, 0]]) + with self.assertRaises(RuntimeError): + transformation = at.affine_points_fit(p_start, p_end) diff --git a/tests/test_datasets/parameters_rbf_cube_out_true.prm b/tests/test_datasets/parameters_rbf_cube_out_true.prm index 2eae9a0..463f2bf 100644 --- a/tests/test_datasets/parameters_rbf_cube_out_true.prm +++ b/tests/test_datasets/parameters_rbf_cube_out_true.prm @@ -4,7 +4,7 @@ # basis funtion is the name of the basis functions to use in the transformation. The functions # implemented so far are: gaussian_spline, multi_quadratic_biharmonic_spline, -# inv_multi_quadratic_biharmonic_spline, thin_plate_spline, beckert_wendland_c2_basis, polyharmonic_spline. +# inv_multi_quadratic_biharmonic_spline, thin_plate_spline, beckert_wendland_c2_basis, polyharmonic_spline. # For a comprehensive list with details see the class RBF. basis function: gaussian_spline diff --git a/tests/test_datasets/parameters_rbf_default.prm b/tests/test_datasets/parameters_rbf_default.prm index 7ebed42..d41b03f 100644 --- a/tests/test_datasets/parameters_rbf_default.prm +++ b/tests/test_datasets/parameters_rbf_default.prm @@ -4,7 +4,7 @@ # basis funtion is the name of the basis functions to use in the transformation. The functions # implemented so far are: gaussian_spline, multi_quadratic_biharmonic_spline, -# inv_multi_quadratic_biharmonic_spline, thin_plate_spline, beckert_wendland_c2_basis, polyharmonic_spline. +# inv_multi_quadratic_biharmonic_spline, thin_plate_spline, beckert_wendland_c2_basis, polyharmonic_spline. # For a comprehensive list with details see the class RBF. basis function: gaussian_spline diff --git a/tests/test_ffdparams.py b/tests/test_ffdparams.py index a7ab0bb..37c3332 100644 --- a/tests/test_ffdparams.py +++ b/tests/test_ffdparams.py @@ -11,374 +11,354 @@ class TestFFDParameters(TestCase): - def test_class_members_default_n_control_points(self): - params = ffdp.FFDParameters() - assert np.array_equal(params.n_control_points, [2, 2, 2]) - - def test_class_members_default_conversion_unit(self): - params = ffdp.FFDParameters() - assert params.conversion_unit == 1. - - def test_class_members_default_lenght_box(self): - params = ffdp.FFDParameters() - assert np.array_equal(params.lenght_box, np.ones(3)) - - def test_class_members_default_origin_box(self): - params = ffdp.FFDParameters() - assert np.array_equal(params.origin_box, np.zeros(3)) - - def test_class_members_default_rot_angle(self): - params = ffdp.FFDParameters() - assert np.array_equal(params.rot_angle, np.zeros(3)) - - def test_class_members_default_array_mu_x(self): - params = ffdp.FFDParameters() - np.testing.assert_array_almost_equal( - params.array_mu_x, np.zeros((2, 2, 2)) - ) - - def test_class_members_default_array_mu_y(self): - params = ffdp.FFDParameters() - np.testing.assert_array_almost_equal( - params.array_mu_y, np.zeros((2, 2, 2)) - ) - - def test_class_members_default_array_mu_z(self): - params = ffdp.FFDParameters() - np.testing.assert_array_almost_equal( - params.array_mu_z, np.zeros((2, 2, 2)) - ) - - def test_class_members_default_psi_mapping(self): - params = ffdp.FFDParameters() - np.testing.assert_array_almost_equal( - params.psi_mapping, np.diag([1, 1, 1]) - ) - - def test_class_members_default_inv_psi_mapping(self): - params = ffdp.FFDParameters() - np.testing.assert_array_almost_equal( - params.inv_psi_mapping, np.diag([1, 1, 1]) - ) - - def test_class_members_default_rotation_matrix(self): - params = ffdp.FFDParameters() - np.testing.assert_array_almost_equal(params.rotation_matrix, np.eye(3)) - - def test_class_members_default_position_vertex_0(self): - params = ffdp.FFDParameters() - np.testing.assert_array_almost_equal( - params.position_vertex_0, np.zeros(3) - ) - - def test_class_members_default_position_vertex_1(self): - params = ffdp.FFDParameters() - np.testing.assert_array_almost_equal( - params.position_vertex_1, np.array([1., 0., 0.]) - ) - - def test_class_members_default_position_vertex_2(self): - params = ffdp.FFDParameters() - np.testing.assert_array_almost_equal( - params.position_vertex_2, np.array([0., 1., 0.]) - ) - - def test_class_members_default_position_vertex_3(self): - params = ffdp.FFDParameters() - np.testing.assert_array_almost_equal( - params.position_vertex_3, np.array([0., 0., 1.]) - ) - - def test_class_members_generic_n_control_points(self): - params = ffdp.FFDParameters([2, 3, 5]) - assert np.array_equal(params.n_control_points, [2, 3, 5]) - - def test_class_members_generic_array_mu_x(self): - params = ffdp.FFDParameters([2, 3, 5]) - np.testing.assert_array_almost_equal( - params.array_mu_x, np.zeros((2, 3, 5)) - ) - - def test_class_members_generic_array_mu_y(self): - params = ffdp.FFDParameters([2, 3, 5]) - np.testing.assert_array_almost_equal( - params.array_mu_y, np.zeros((2, 3, 5)) - ) - - def test_class_members_generic_array_mu_z(self): - params = ffdp.FFDParameters([2, 3, 5]) - np.testing.assert_array_almost_equal( - params.array_mu_z, np.zeros((2, 3, 5)) - ) - - def test_read_parameters_conversion_unit(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.conversion_unit == 1. - - def test_read_parameters_n_control_points(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert np.array_equal(params.n_control_points, [3, 2, 2]) - - def test_read_parameters_lenght_box_x(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert np.array_equal(params.lenght_box, [45.0, 90.0, 90.0]) - - def test_read_parameters_origin_box(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - origin_box_exact = np.array([-20.0, -55.0, -45.0]) - np.testing.assert_array_almost_equal( - params.origin_box, origin_box_exact - ) - - def test_read_parameters_rot_angle_x(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert np.array_equal(params.rot_angle, [20.3, 11.0, 0.]) - - def test_read_parameters_array_mu_x(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - array_mu_x_exact = np.array( - [0.2, 0., 0., 0., 0.5, 0., 0., 0., 1., 0., 0., 0.] - ).reshape((3, 2, 2)) - print(params.array_mu_x) - np.testing.assert_array_almost_equal( - params.array_mu_x, array_mu_x_exact - ) - - def test_read_parameters_array_mu_y(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - array_mu_y_exact = np.array( - [0., 0., 0.5555555555, 0., 0., 0., 0., 0., -1., 0., 0., 0.] - ).reshape((3, 2, 2)) - np.testing.assert_array_almost_equal( - params.array_mu_y, array_mu_y_exact - ) - - def test_read_parameters_array_mu_z(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - array_mu_z_exact = np.array( - [0., -0.2, 0., -0.45622985, 0., 0., 0., 0., -1.22, 0., -1., 0.] - ).reshape((3, 2, 2)) - np.testing.assert_array_almost_equal( - params.array_mu_z, array_mu_z_exact - ) - - def test_read_parameters_psi_mapping(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - psi_mapping_exact = np.diag([0.02222222, 0.01111111, 0.01111111]) - np.testing.assert_array_almost_equal( - params.psi_mapping, psi_mapping_exact - ) - - def test_read_parameters_inv_psi_mapping(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - inv_psi_mapping_exact = np.diag([45., 90., 90.]) - np.testing.assert_array_almost_equal( - params.inv_psi_mapping, inv_psi_mapping_exact - ) - - def test_read_parameters_rotation_matrix(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - rotation_matrix_exact = np.array([ - [ 0.98162718, 0. , 0.190809 ], - [ 0.06619844, 0.93788893, -0.34056147], - [-0.17895765, 0.34693565, 0.92065727] - ]) - np.testing.assert_array_almost_equal( - params.rotation_matrix, rotation_matrix_exact - ) - - def test_read_parameters_position_vertex_0_origin(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - np.testing.assert_array_almost_equal( - params.position_vertex_0, params.origin_box - ) - - def test_read_parameters_position_vertex_0(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - position_vertex_0_exact = np.array([-20.0, -55.0, -45.0]) - np.testing.assert_array_almost_equal( - params.position_vertex_0, position_vertex_0_exact - ) - - def test_read_parameters_position_vertex_1(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - position_vertex_1_exact = np.array( - [24.17322326, -52.02107006, -53.05309404] - ) - np.testing.assert_array_almost_equal( - params.position_vertex_1, position_vertex_1_exact - ) - - def test_read_parameters_position_vertex_2(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - position_vertex_2_exact = np.array([-20., 29.41000412, -13.77579136]) - np.testing.assert_array_almost_equal( - params.position_vertex_2, position_vertex_2_exact - ) - - def test_read_parameters_position_vertex_3(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - position_vertex_3_exact = np.array( - [-2.82719042, -85.65053198, 37.85915459] - ) - np.testing.assert_array_almost_equal( - params.position_vertex_3, position_vertex_3_exact - ) - - def test_read_parameters_failing_filename_type(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - with self.assertRaises(TypeError): - params.read_parameters(3) - - def test_read_parameters_filename_default_existance(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters() - outfilename = 'parameters.prm' - assert os.path.isfile(outfilename) - os.remove(outfilename) - - def test_read_parameters_filename_default(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters() - outfilename = 'parameters.prm' - outfilename_expected = 'tests/test_datasets/parameters_default.prm' - - self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) - os.remove(outfilename) - - def test_write_parameters_failing_filename_type(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - with self.assertRaises(TypeError): - params.write_parameters(5) - - def test_write_parameters_filename_default_existance(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.write_parameters() - outfilename = 'parameters.prm' - assert os.path.isfile(outfilename) - os.remove(outfilename) - - def test_write_parameters_filename_default(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.write_parameters() - outfilename = 'parameters.prm' - outfilename_expected = 'tests/test_datasets/parameters_default.prm' - - self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) - os.remove(outfilename) - - def test_write_parameters(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - - outfilename = 'tests/test_datasets/parameters_sphere_out.prm' - outfilename_expected = 'tests/test_datasets/parameters_sphere_out_true.prm' - params.write_parameters(outfilename) - - self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) - os.remove(outfilename) - - def test_print(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - print(params) - - def test_build_bounding_box_1(self): - origin = np.array([0., 0., 0.]) - tops = np.array([1., 1., 1.]) - cube = BRepPrimAPI_MakeBox(*tops).Shape() - params = ffdp.FFDParameters() - params.build_bounding_box(cube) - - np.testing.assert_array_almost_equal(params.lenght_box, tops, decimal=5) - - def test_build_bounding_box_2(self): - origin = np.array([0., 0., 0.]) - tops = np.array([1., 1., 1.]) - cube = BRepPrimAPI_MakeBox(*tops).Shape() - params = ffdp.FFDParameters() - params.build_bounding_box(cube) - - np.testing.assert_almost_equal(params.position_vertex_0, origin, decimal=5) - np.testing.assert_almost_equal(params.position_vertex_1, [1., 0., 0.], decimal=5) - np.testing.assert_almost_equal(params.position_vertex_2, [0., 1., 0.], decimal=5) - np.testing.assert_almost_equal(params.position_vertex_3, [0., 0., 1.], decimal=5) - - def test_set_box_dimension(self): - origin = np.array([0., 0., 0.]) - tops = np.array([10., 10., 10.]) - params = ffdp.FFDParameters() - params.origin_box = origin - params._set_box_dimensions(origin, tops) - np.testing.assert_array_almost_equal(params.lenght_box, tops) - - def test_set_position_of_vertices(self): - vertex_0 = [0., 0., 0.] - vertex_1 = [1., 0., 0.] - vertex_2 = [0., 1., 0.] - vertex_3 = [0., 0., 1.] - tops = np.array([1., 1., 1.]) - params = ffdp.FFDParameters() - params.origin_box = vertex_0 - params._set_box_dimensions(vertex_0, tops) - params._set_position_of_vertices() - np.testing.assert_equal(params.position_vertex_0, vertex_0) - np.testing.assert_equal(params.position_vertex_1, vertex_1) - np.testing.assert_equal(params.position_vertex_2, vertex_2) - np.testing.assert_equal(params.position_vertex_3, vertex_3) - - def test_set_mapping(self): - origin = np.array([0., 0., 0.]) - tops = np.array([10., 10., 10.]) - params = ffdp.FFDParameters() - params.origin_box = origin - params._set_box_dimensions(origin, tops) - params._set_mapping() - for i in range(3): - self.assertEqual(params.psi_mapping[i][i], 1. / tops[i]) - self.assertEqual(params.inv_psi_mapping[i][i], tops[i]) - - def test_set_modification_parameters_to_zero(self): - params = ffdp.FFDParameters([5, 5, 5]) - params._set_transformation_params_to_zero() - np.testing.assert_almost_equal(params.array_mu_x, np.zeros(shape=(5, 5, 5))) - np.testing.assert_almost_equal(params.array_mu_y, np.zeros(shape=(5, 5, 5))) - np.testing.assert_almost_equal(params.array_mu_z, np.zeros(shape=(5, 5, 5))) - - def test_calculate_bb_dimensions(self): - min_vals = np.zeros(3) - max_vals = np.ones(3) - cube = BRepPrimAPI_MakeBox(1, 1, 1).Shape() - params = ffdp.FFDParameters() - xyz_min, xyz_max = params._calculate_bb_dimension(cube) - np.testing.assert_almost_equal(xyz_min, min_vals, decimal=5) - np.testing.assert_almost_equal(xyz_max, max_vals, decimal=5) - - def test_calculate_bb_dimensions_triangulate(self): - a = gp_Pnt(-1, -1, -1) - b = gp_Pnt(3, 3, 3) - - box = BRepPrimAPI_MakeBox(a, b).Shape() - sphere = BRepPrimAPI_MakeSphere(3).Shape() - section = BRepAlgoAPI_Cut(box, sphere).Shape() - params = ffdp.FFDParameters() - xyz_min, xyz_max = params._calculate_bb_dimension(section, triangulate=True) - correct_min = -1 * np.ones(3) - correct_max = 3 * np.ones(3) - np.testing.assert_almost_equal(xyz_min, correct_min, decimal=1) - np.testing.assert_almost_equal(xyz_max, correct_max, decimal=1) + def test_class_members_default_n_control_points(self): + params = ffdp.FFDParameters() + assert np.array_equal(params.n_control_points, [2, 2, 2]) + + def test_class_members_default_conversion_unit(self): + params = ffdp.FFDParameters() + assert params.conversion_unit == 1. + + def test_class_members_default_lenght_box(self): + params = ffdp.FFDParameters() + assert np.array_equal(params.lenght_box, np.ones(3)) + + def test_class_members_default_origin_box(self): + params = ffdp.FFDParameters() + assert np.array_equal(params.origin_box, np.zeros(3)) + + def test_class_members_default_rot_angle(self): + params = ffdp.FFDParameters() + assert np.array_equal(params.rot_angle, np.zeros(3)) + + def test_class_members_default_array_mu_x(self): + params = ffdp.FFDParameters() + np.testing.assert_array_almost_equal(params.array_mu_x, + np.zeros((2, 2, 2))) + + def test_class_members_default_array_mu_y(self): + params = ffdp.FFDParameters() + np.testing.assert_array_almost_equal(params.array_mu_y, + np.zeros((2, 2, 2))) + + def test_class_members_default_array_mu_z(self): + params = ffdp.FFDParameters() + np.testing.assert_array_almost_equal(params.array_mu_z, + np.zeros((2, 2, 2))) + + def test_class_members_default_psi_mapping(self): + params = ffdp.FFDParameters() + np.testing.assert_array_almost_equal(params.psi_mapping, + np.diag([1, 1, 1])) + + def test_class_members_default_inv_psi_mapping(self): + params = ffdp.FFDParameters() + np.testing.assert_array_almost_equal(params.inv_psi_mapping, + np.diag([1, 1, 1])) + + def test_class_members_default_rotation_matrix(self): + params = ffdp.FFDParameters() + np.testing.assert_array_almost_equal(params.rotation_matrix, np.eye(3)) + + def test_class_members_default_position_vertex_0(self): + params = ffdp.FFDParameters() + np.testing.assert_array_almost_equal(params.position_vertex_0, + np.zeros(3)) + + def test_class_members_default_position_vertex_1(self): + params = ffdp.FFDParameters() + np.testing.assert_array_almost_equal(params.position_vertex_1, + np.array([1., 0., 0.])) + + def test_class_members_default_position_vertex_2(self): + params = ffdp.FFDParameters() + np.testing.assert_array_almost_equal(params.position_vertex_2, + np.array([0., 1., 0.])) + + def test_class_members_default_position_vertex_3(self): + params = ffdp.FFDParameters() + np.testing.assert_array_almost_equal(params.position_vertex_3, + np.array([0., 0., 1.])) + + def test_class_members_generic_n_control_points(self): + params = ffdp.FFDParameters([2, 3, 5]) + assert np.array_equal(params.n_control_points, [2, 3, 5]) + + def test_class_members_generic_array_mu_x(self): + params = ffdp.FFDParameters([2, 3, 5]) + np.testing.assert_array_almost_equal(params.array_mu_x, + np.zeros((2, 3, 5))) + + def test_class_members_generic_array_mu_y(self): + params = ffdp.FFDParameters([2, 3, 5]) + np.testing.assert_array_almost_equal(params.array_mu_y, + np.zeros((2, 3, 5))) + + def test_class_members_generic_array_mu_z(self): + params = ffdp.FFDParameters([2, 3, 5]) + np.testing.assert_array_almost_equal(params.array_mu_z, + np.zeros((2, 3, 5))) + + def test_read_parameters_conversion_unit(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.conversion_unit == 1. + + def test_read_parameters_n_control_points(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert np.array_equal(params.n_control_points, [3, 2, 2]) + + def test_read_parameters_lenght_box_x(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert np.array_equal(params.lenght_box, [45.0, 90.0, 90.0]) + + def test_read_parameters_origin_box(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + origin_box_exact = np.array([-20.0, -55.0, -45.0]) + np.testing.assert_array_almost_equal(params.origin_box, + origin_box_exact) + + def test_read_parameters_rot_angle_x(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert np.array_equal(params.rot_angle, [20.3, 11.0, 0.]) + + def test_read_parameters_array_mu_x(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + array_mu_x_exact = np.array( + [0.2, 0., 0., 0., 0.5, 0., 0., 0., 1., 0., 0., 0.]).reshape((3, 2, + 2)) + print(params.array_mu_x) + np.testing.assert_array_almost_equal(params.array_mu_x, + array_mu_x_exact) + + def test_read_parameters_array_mu_y(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + array_mu_y_exact = np.array( + [0., 0., 0.5555555555, 0., 0., 0., 0., 0., -1., 0., 0., + 0.]).reshape((3, 2, 2)) + np.testing.assert_array_almost_equal(params.array_mu_y, + array_mu_y_exact) + + def test_read_parameters_array_mu_z(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + array_mu_z_exact = np.array( + [0., -0.2, 0., -0.45622985, 0., 0., 0., 0., -1.22, 0., -1., + 0.]).reshape((3, 2, 2)) + np.testing.assert_array_almost_equal(params.array_mu_z, + array_mu_z_exact) + + def test_read_parameters_psi_mapping(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + psi_mapping_exact = np.diag([0.02222222, 0.01111111, 0.01111111]) + np.testing.assert_array_almost_equal(params.psi_mapping, + psi_mapping_exact) + + def test_read_parameters_inv_psi_mapping(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + inv_psi_mapping_exact = np.diag([45., 90., 90.]) + np.testing.assert_array_almost_equal(params.inv_psi_mapping, + inv_psi_mapping_exact) + + def test_read_parameters_rotation_matrix(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + rotation_matrix_exact = np.array( + [[0.98162718, 0., 0.190809], [0.06619844, 0.93788893, -0.34056147], + [-0.17895765, 0.34693565, 0.92065727]]) + np.testing.assert_array_almost_equal(params.rotation_matrix, + rotation_matrix_exact) + + def test_read_parameters_position_vertex_0_origin(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + np.testing.assert_array_almost_equal(params.position_vertex_0, + params.origin_box) + + def test_read_parameters_position_vertex_0(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + position_vertex_0_exact = np.array([-20.0, -55.0, -45.0]) + np.testing.assert_array_almost_equal(params.position_vertex_0, + position_vertex_0_exact) + + def test_read_parameters_position_vertex_1(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + position_vertex_1_exact = np.array( + [24.17322326, -52.02107006, -53.05309404]) + np.testing.assert_array_almost_equal(params.position_vertex_1, + position_vertex_1_exact) + + def test_read_parameters_position_vertex_2(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + position_vertex_2_exact = np.array([-20., 29.41000412, -13.77579136]) + np.testing.assert_array_almost_equal(params.position_vertex_2, + position_vertex_2_exact) + + def test_read_parameters_position_vertex_3(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + position_vertex_3_exact = np.array( + [-2.82719042, -85.65053198, 37.85915459]) + np.testing.assert_array_almost_equal(params.position_vertex_3, + position_vertex_3_exact) + + def test_read_parameters_failing_filename_type(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + with self.assertRaises(TypeError): + params.read_parameters(3) + + def test_read_parameters_filename_default_existance(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters() + outfilename = 'parameters.prm' + assert os.path.isfile(outfilename) + os.remove(outfilename) + + def test_read_parameters_filename_default(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters() + outfilename = 'parameters.prm' + outfilename_expected = 'tests/test_datasets/parameters_default.prm' + + self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) + os.remove(outfilename) + + def test_write_parameters_failing_filename_type(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + with self.assertRaises(TypeError): + params.write_parameters(5) + + def test_write_parameters_filename_default_existance(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.write_parameters() + outfilename = 'parameters.prm' + assert os.path.isfile(outfilename) + os.remove(outfilename) + + def test_write_parameters_filename_default(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.write_parameters() + outfilename = 'parameters.prm' + outfilename_expected = 'tests/test_datasets/parameters_default.prm' + + self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) + os.remove(outfilename) + + def test_write_parameters(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + + outfilename = 'tests/test_datasets/parameters_sphere_out.prm' + outfilename_expected = 'tests/test_datasets/parameters_sphere_out_true.prm' + params.write_parameters(outfilename) + + self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) + os.remove(outfilename) + + def test_print(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + print(params) + + def test_build_bounding_box_1(self): + origin = np.array([0., 0., 0.]) + tops = np.array([1., 1., 1.]) + cube = BRepPrimAPI_MakeBox(*tops).Shape() + params = ffdp.FFDParameters() + params.build_bounding_box(cube) + + np.testing.assert_array_almost_equal(params.lenght_box, tops, decimal=5) + + def test_build_bounding_box_2(self): + origin = np.array([0., 0., 0.]) + tops = np.array([1., 1., 1.]) + cube = BRepPrimAPI_MakeBox(*tops).Shape() + params = ffdp.FFDParameters() + params.build_bounding_box(cube) + + np.testing.assert_almost_equal( + params.position_vertex_0, origin, decimal=5) + np.testing.assert_almost_equal( + params.position_vertex_1, [1., 0., 0.], decimal=5) + np.testing.assert_almost_equal( + params.position_vertex_2, [0., 1., 0.], decimal=5) + np.testing.assert_almost_equal( + params.position_vertex_3, [0., 0., 1.], decimal=5) + + def test_set_box_dimension(self): + origin = np.array([0., 0., 0.]) + tops = np.array([10., 10., 10.]) + params = ffdp.FFDParameters() + params.origin_box = origin + params._set_box_dimensions(origin, tops) + np.testing.assert_array_almost_equal(params.lenght_box, tops) + + def test_set_position_of_vertices(self): + vertex_0 = [0., 0., 0.] + vertex_1 = [1., 0., 0.] + vertex_2 = [0., 1., 0.] + vertex_3 = [0., 0., 1.] + tops = np.array([1., 1., 1.]) + params = ffdp.FFDParameters() + params.origin_box = vertex_0 + params._set_box_dimensions(vertex_0, tops) + params._set_position_of_vertices() + np.testing.assert_equal(params.position_vertex_0, vertex_0) + np.testing.assert_equal(params.position_vertex_1, vertex_1) + np.testing.assert_equal(params.position_vertex_2, vertex_2) + np.testing.assert_equal(params.position_vertex_3, vertex_3) + + def test_set_mapping(self): + origin = np.array([0., 0., 0.]) + tops = np.array([10., 10., 10.]) + params = ffdp.FFDParameters() + params.origin_box = origin + params._set_box_dimensions(origin, tops) + params._set_mapping() + for i in range(3): + self.assertEqual(params.psi_mapping[i][i], 1. / tops[i]) + self.assertEqual(params.inv_psi_mapping[i][i], tops[i]) + + def test_set_modification_parameters_to_zero(self): + params = ffdp.FFDParameters([5, 5, 5]) + params._set_transformation_params_to_zero() + np.testing.assert_almost_equal( + params.array_mu_x, np.zeros(shape=(5, 5, 5))) + np.testing.assert_almost_equal( + params.array_mu_y, np.zeros(shape=(5, 5, 5))) + np.testing.assert_almost_equal( + params.array_mu_z, np.zeros(shape=(5, 5, 5))) + + def test_calculate_bb_dimensions(self): + min_vals = np.zeros(3) + max_vals = np.ones(3) + cube = BRepPrimAPI_MakeBox(1, 1, 1).Shape() + params = ffdp.FFDParameters() + xyz_min, xyz_max = params._calculate_bb_dimension(cube) + np.testing.assert_almost_equal(xyz_min, min_vals, decimal=5) + np.testing.assert_almost_equal(xyz_max, max_vals, decimal=5) + + def test_calculate_bb_dimensions_triangulate(self): + a = gp_Pnt(-1, -1, -1) + b = gp_Pnt(3, 3, 3) + + box = BRepPrimAPI_MakeBox(a, b).Shape() + sphere = BRepPrimAPI_MakeSphere(3).Shape() + section = BRepAlgoAPI_Cut(box, sphere).Shape() + params = ffdp.FFDParameters() + xyz_min, xyz_max = params._calculate_bb_dimension( + section, triangulate=True) + correct_min = -1 * np.ones(3) + correct_max = 3 * np.ones(3) + np.testing.assert_almost_equal(xyz_min, correct_min, decimal=1) + np.testing.assert_almost_equal(xyz_max, correct_max, decimal=1) diff --git a/tests/test_filehandler.py b/tests/test_filehandler.py index ca23cd0..46ee24f 100644 --- a/tests/test_filehandler.py +++ b/tests/test_filehandler.py @@ -6,25 +6,25 @@ class TestFilehandler(TestCase): - def test_base_class_infile(self): - file_handler = fh.FileHandler() - self.assertIsNone(file_handler.infile) - - def test_base_class_outfile(self): - file_handler = fh.FileHandler() - self.assertIsNone(file_handler.outfile) - - def test_base_class_extension(self): - file_handler = fh.FileHandler() - self.assertListEqual(file_handler.extensions, []) - - def test_base_class_parse(self): - file_handler = fh.FileHandler() - with self.assertRaises(NotImplementedError): - file_handler.parse('input') - - def test_base_class_write(self): - file_handler = fh.FileHandler() - mesh_points = np.zeros((3, 3)) - with self.assertRaises(NotImplementedError): - file_handler.write(mesh_points, 'output') + def test_base_class_infile(self): + file_handler = fh.FileHandler() + self.assertIsNone(file_handler.infile) + + def test_base_class_outfile(self): + file_handler = fh.FileHandler() + self.assertIsNone(file_handler.outfile) + + def test_base_class_extension(self): + file_handler = fh.FileHandler() + self.assertListEqual(file_handler.extensions, []) + + def test_base_class_parse(self): + file_handler = fh.FileHandler() + with self.assertRaises(NotImplementedError): + file_handler.parse('input') + + def test_base_class_write(self): + file_handler = fh.FileHandler() + mesh_points = np.zeros((3, 3)) + with self.assertRaises(NotImplementedError): + file_handler.write(mesh_points, 'output') diff --git a/tests/test_freeform.py b/tests/test_freeform.py index 5eb081b..88905f9 100644 --- a/tests/test_freeform.py +++ b/tests/test_freeform.py @@ -6,57 +6,49 @@ class TestFreeform(TestCase): - def test_ffd_parameters_member(self): - params = ffdp.FFDParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_test_ffd_identity.prm' - ) - mesh_points = np.load('tests/test_datasets/meshpoints_sphere_orig.npy') - free_form = ffd.FFD(params, mesh_points) - assert free_form.parameters == params + def test_ffd_parameters_member(self): + params = ffdp.FFDParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_test_ffd_identity.prm') + mesh_points = np.load('tests/test_datasets/meshpoints_sphere_orig.npy') + free_form = ffd.FFD(params, mesh_points) + assert free_form.parameters == params - def test_ffd_original_mesh_points_member(self): - params = ffdp.FFDParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_test_ffd_identity.prm' - ) - mesh_points = np.load('tests/test_datasets/meshpoints_sphere_orig.npy') - free_form = ffd.FFD(params, mesh_points) - np.testing.assert_array_almost_equal( - free_form.original_mesh_points, mesh_points - ) + def test_ffd_original_mesh_points_member(self): + params = ffdp.FFDParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_test_ffd_identity.prm') + mesh_points = np.load('tests/test_datasets/meshpoints_sphere_orig.npy') + free_form = ffd.FFD(params, mesh_points) + np.testing.assert_array_almost_equal(free_form.original_mesh_points, + mesh_points) - def test_ffd_default_modified_mesh_points_member(self): - params = ffdp.FFDParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_test_ffd_identity.prm' - ) - mesh_points = np.load('tests/test_datasets/meshpoints_sphere_orig.npy') - free_form = ffd.FFD(params, mesh_points) - assert free_form.modified_mesh_points == None + def test_ffd_default_modified_mesh_points_member(self): + params = ffdp.FFDParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_test_ffd_identity.prm') + mesh_points = np.load('tests/test_datasets/meshpoints_sphere_orig.npy') + free_form = ffd.FFD(params, mesh_points) + assert free_form.modified_mesh_points == None - def test_ffd_modified_mesh_points_member(self): - params = ffdp.FFDParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_test_ffd_identity.prm' - ) - mesh_points = np.load('tests/test_datasets/meshpoints_sphere_orig.npy') - free_form = ffd.FFD(params, mesh_points) - free_form.perform() - np.testing.assert_array_almost_equal( - free_form.modified_mesh_points, mesh_points - ) + def test_ffd_modified_mesh_points_member(self): + params = ffdp.FFDParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_test_ffd_identity.prm') + mesh_points = np.load('tests/test_datasets/meshpoints_sphere_orig.npy') + free_form = ffd.FFD(params, mesh_points) + free_form.perform() + np.testing.assert_array_almost_equal(free_form.modified_mesh_points, + mesh_points) - def test_ffd_sphere_mod(self): - params = ffdp.FFDParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_test_ffd_sphere.prm' - ) - mesh_points = np.load('tests/test_datasets/meshpoints_sphere_orig.npy') - mesh_points_ref = np.load( - 'tests/test_datasets/meshpoints_sphere_mod.npy' - ) - free_form = ffd.FFD(params, mesh_points) - free_form.perform() - mesh_points_test = free_form.modified_mesh_points - np.testing.assert_array_almost_equal(mesh_points_test, mesh_points_ref) + def test_ffd_sphere_mod(self): + params = ffdp.FFDParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_test_ffd_sphere.prm') + mesh_points = np.load('tests/test_datasets/meshpoints_sphere_orig.npy') + mesh_points_ref = np.load( + 'tests/test_datasets/meshpoints_sphere_mod.npy') + free_form = ffd.FFD(params, mesh_points) + free_form.perform() + mesh_points_test = free_form.modified_mesh_points + np.testing.assert_array_almost_equal(mesh_points_test, mesh_points_ref) diff --git a/tests/test_gui.py b/tests/test_gui.py index 8f0b3c5..1498b82 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -4,74 +4,74 @@ class TestGui(TestCase): - def test_gui_init_string_1(self): - gui_handler = gui.Gui() - assert gui_handler.root.title() == 'PyGeM' + def test_gui_init_string_1(self): + gui_handler = gui.Gui() + assert gui_handler.root.title() == 'PyGeM' - def test_gui_init_string_2(self): - gui_handler = gui.Gui() - assert gui_handler.filename_geometry.get() == '' + def test_gui_init_string_2(self): + gui_handler = gui.Gui() + assert gui_handler.filename_geometry.get() == '' - def test_gui_init_string_3(self): - gui_handler = gui.Gui() - assert gui_handler.filename_parameters.get() == '' + def test_gui_init_string_3(self): + gui_handler = gui.Gui() + assert gui_handler.filename_parameters.get() == '' - def test_gui_init_string_4(self): - gui_handler = gui.Gui() - assert gui_handler.outfilename.get() == '' + def test_gui_init_string_4(self): + gui_handler = gui.Gui() + assert gui_handler.outfilename.get() == '' - def test_gui_init_string_5(self): - gui_handler = gui.Gui() - assert gui_handler.outfilename_lattice_orig.get() == '' + def test_gui_init_string_5(self): + gui_handler = gui.Gui() + assert gui_handler.outfilename_lattice_orig.get() == '' - def test_gui_init_string_6(self): - gui_handler = gui.Gui() - assert gui_handler.outfilename_lattice_mod.get() == '' + def test_gui_init_string_6(self): + gui_handler = gui.Gui() + assert gui_handler.outfilename_lattice_mod.get() == '' - def test_gui_init_string_7(self): - gui_handler = gui.Gui() - assert gui_handler.print_geometry_path.get() == '' + def test_gui_init_string_7(self): + gui_handler = gui.Gui() + assert gui_handler.print_geometry_path.get() == '' - def test_gui_init_string_8(self): - gui_handler = gui.Gui() - assert gui_handler.print_parameter_path.get() == '' + def test_gui_init_string_8(self): + gui_handler = gui.Gui() + assert gui_handler.print_parameter_path.get() == '' - def test_gui_init_string_9(self): - gui_handler = gui.Gui() - assert gui_handler.url == 'https://github.com/mathLab/PyGeM' + def test_gui_init_string_9(self): + gui_handler = gui.Gui() + assert gui_handler.url == 'https://github.com/mathLab/PyGeM' - def test_gui_init_int_1(self): - gui_handler = gui.Gui() - assert gui_handler.check_var_dump_orig.get() == 0 + def test_gui_init_int_1(self): + gui_handler = gui.Gui() + assert gui_handler.check_var_dump_orig.get() == 0 - def test_gui_init_int_2(self): - gui_handler = gui.Gui() - assert gui_handler.check_var_dump_morphed.get() == 0 + def test_gui_init_int_2(self): + gui_handler = gui.Gui() + assert gui_handler.check_var_dump_morphed.get() == 0 - def test_gui_init_none_1(self): - gui_handler = gui.Gui() - assert gui_handler.label_geo == None + def test_gui_init_none_1(self): + gui_handler = gui.Gui() + assert gui_handler.label_geo == None - def test_gui_init_none_2(self): - gui_handler = gui.Gui() - assert gui_handler.label_params == None + def test_gui_init_none_2(self): + gui_handler = gui.Gui() + assert gui_handler.label_params == None - def test_gui_init_none_3(self): - gui_handler = gui.Gui() - assert gui_handler.logo_panel == None + def test_gui_init_none_3(self): + gui_handler = gui.Gui() + assert gui_handler.logo_panel == None - def test_gui_init_none_4(self): - gui_handler = gui.Gui() - assert gui_handler.img == None + def test_gui_init_none_4(self): + gui_handler = gui.Gui() + assert gui_handler.img == None - def test_gui_init_none_5(self): - gui_handler = gui.Gui() - assert gui_handler.orig_geo_frame == None + def test_gui_init_none_5(self): + gui_handler = gui.Gui() + assert gui_handler.orig_geo_frame == None - def test_gui_init_none_6(self): - gui_handler = gui.Gui() - assert gui_handler.mod_geo_frame == None + def test_gui_init_none_6(self): + gui_handler = gui.Gui() + assert gui_handler.mod_geo_frame == None - def test_gui_main(self): - interface = gui.Gui() - interface._main() + def test_gui_main(self): + interface = gui.Gui() + interface._main() diff --git a/tests/test_idw.py b/tests/test_idw.py index 3156d0a..bd3994b 100644 --- a/tests/test_idw.py +++ b/tests/test_idw.py @@ -4,35 +4,35 @@ from pygem.params_idw import IDWParameters import numpy as np + class TestIDW(TestCase): - def get_cube_mesh_points(self): - # Points that define a cube - nx, ny, nz = (20, 20, 20) - mesh = np.zeros((nx * ny * nz, 3)) - xv = np.linspace(0, 1, nx) - yv = np.linspace(0, 1, ny) - zv = np.linspace(0, 1, nz) - z, y, x = np.meshgrid(zv, yv, xv) - mesh = np.array([x.ravel(), y.ravel(), z.ravel()]) - original_mesh_points = mesh.T - return original_mesh_points + def get_cube_mesh_points(self): + # Points that define a cube + nx, ny, nz = (20, 20, 20) + mesh = np.zeros((nx * ny * nz, 3)) + xv = np.linspace(0, 1, nx) + yv = np.linspace(0, 1, ny) + zv = np.linspace(0, 1, nz) + z, y, x = np.meshgrid(zv, yv, xv) + mesh = np.array([x.ravel(), y.ravel(), z.ravel()]) + original_mesh_points = mesh.T + return original_mesh_points - def test_idw(self): - params = IDWParameters() - params.read_parameters('tests/test_datasets/parameters_idw_default.prm') - idw = IDW(params, self.get_cube_mesh_points()) + def test_idw(self): + params = IDWParameters() + params.read_parameters('tests/test_datasets/parameters_idw_default.prm') + idw = IDW(params, self.get_cube_mesh_points()) - def test_idw_perform(self): - params = IDWParameters() - params.read_parameters('tests/test_datasets/parameters_idw_default.prm') - IDW(params, self.get_cube_mesh_points()).perform() + def test_idw_perform(self): + params = IDWParameters() + params.read_parameters('tests/test_datasets/parameters_idw_default.prm') + IDW(params, self.get_cube_mesh_points()).perform() - def test_idw_perform_deform(self): - params = IDWParameters() - expected_stretch = [ 1.19541593, 1.36081491, 1.42095073] - params.read_parameters('tests/test_datasets/parameters_idw_deform.prm') - idw = IDW(params, self.get_cube_mesh_points()) - idw.perform() - np.testing.assert_array_almost_equal( - idw.modified_mesh_points[-3], expected_stretch - ) + def test_idw_perform_deform(self): + params = IDWParameters() + expected_stretch = [1.19541593, 1.36081491, 1.42095073] + params.read_parameters('tests/test_datasets/parameters_idw_deform.prm') + idw = IDW(params, self.get_cube_mesh_points()) + idw.perform() + np.testing.assert_array_almost_equal(idw.modified_mesh_points[-3], + expected_stretch) diff --git a/tests/test_idwparams.py b/tests/test_idwparams.py index 5dd7f78..d0d8218 100644 --- a/tests/test_idwparams.py +++ b/tests/test_idwparams.py @@ -7,89 +7,62 @@ class TestIDWParameters(TestCase): - def test_class_members_default_p(self): - params = IDWParameters() - assert params.power == 2 + def test_class_members_default_p(self): + params = IDWParameters() + assert params.power == 2 - def test_class_members_default_original_points(self): - params = IDWParameters() - cube_vertices = np.array([ - [0., 0., 0.], - [0., 0., 1.], - [0., 1., 0.], - [1., 0., 0.], - [0., 1., 1.], - [1., 0., 1.], - [1., 1., 0.], - [1., 1., 1.] - ]) - np.testing.assert_equal( - params.original_control_points, cube_vertices - ) + def test_class_members_default_original_points(self): + params = IDWParameters() + cube_vertices = np.array([[0., 0., 0.], [0., 0., 1.], [0., 1., 0.], + [1., 0., 0.], [0., 1., 1.], [1., 0., 1.], + [1., 1., 0.], [1., 1., 1.]]) + np.testing.assert_equal(params.original_control_points, cube_vertices) - def test_class_members_default_deformed_points(self): - params = IDWParameters() - cube_vertices = np.array([ - [0., 0., 0.], - [0., 0., 1.], - [0., 1., 0.], - [1., 0., 0.], - [0., 1., 1.], - [1., 0., 1.], - [1., 1., 0.], - [1., 1., 1.] - ]) - np.testing.assert_equal( - params.deformed_control_points, cube_vertices - ) - - def test_write_parameters_filename_default(self): - params = IDWParameters() - outfilename = 'parameters_rbf.prm' - outfilename_expected = 'tests/test_datasets/parameters_idw_default.prm' - params.write_parameters(outfilename) - self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) - os.remove(outfilename) + def test_class_members_default_deformed_points(self): + params = IDWParameters() + cube_vertices = np.array([[0., 0., 0.], [0., 0., 1.], [0., 1., 0.], + [1., 0., 0.], [0., 1., 1.], [1., 0., 1.], + [1., 1., 0.], [1., 1., 1.]]) + np.testing.assert_equal(params.deformed_control_points, cube_vertices) - def test_write_not_string(self): - params = IDWParameters() - with self.assertRaises(TypeError): - params.write_parameters(5) + def test_write_parameters_filename_default(self): + params = IDWParameters() + outfilename = 'parameters_rbf.prm' + outfilename_expected = 'tests/test_datasets/parameters_idw_default.prm' + params.write_parameters(outfilename) + self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) + os.remove(outfilename) - def test_read_deformed(self): - params = IDWParameters() - filename = 'tests/test_datasets/parameters_idw_deform.prm' - def_vertices = np.array([ - [0., 0., 0.], - [0., 0., 1.], - [0., 1., 0.], - [1., 0., 0.], - [0., 1., 1.], - [1., 0., 1.], - [1., 1., 0.], - [1.5, 1.6, 1.7] - ]) - params.read_parameters(filename) - np.testing.assert_equal( - params.deformed_control_points, def_vertices - ) + def test_write_not_string(self): + params = IDWParameters() + with self.assertRaises(TypeError): + params.write_parameters(5) - def test_read_p(self): - params = IDWParameters() - filename = 'tests/test_datasets/parameters_idw_deform.prm' - params.read_parameters(filename) - assert params.power == 3 + def test_read_deformed(self): + params = IDWParameters() + filename = 'tests/test_datasets/parameters_idw_deform.prm' + def_vertices = np.array([[0., 0., 0.], [0., 0., 1.], [0., 1., 0.], + [1., 0., 0.], [0., 1., 1.], [1., 0., 1.], + [1., 1., 0.], [1.5, 1.6, 1.7]]) + params.read_parameters(filename) + np.testing.assert_equal(params.deformed_control_points, def_vertices) - def test_read_not_string(self): - params = IDWParameters() - with self.assertRaises(TypeError): - params.read_parameters(5) + def test_read_p(self): + params = IDWParameters() + filename = 'tests/test_datasets/parameters_idw_deform.prm' + params.read_parameters(filename) + assert params.power == 3 - def test_read_not_real_file(self): - params = IDWParameters() - with self.assertRaises(IOError): - params.read_parameters('not_real_file') - - def test_print(self): - params = IDWParameters() - print(params) + def test_read_not_string(self): + params = IDWParameters() + with self.assertRaises(TypeError): + params.read_parameters(5) + + def test_read_not_real_file(self): + params = IDWParameters() + with self.assertRaises(IOError): + params.read_parameters('not_real_file') + + def test_print(self): + params = IDWParameters() + print(params) diff --git a/tests/test_igeshandler.py b/tests/test_igeshandler.py index 8fef84d..39fe9a0 100644 --- a/tests/test_igeshandler.py +++ b/tests/test_igeshandler.py @@ -9,229 +9,222 @@ class TestIgesHandler(TestCase): - def test_iges_instantiation(self): - iges_handler = ih.IgesHandler() - - def test_iges_default_extension_member(self): - iges_handler = ih.IgesHandler() - self.assertListEqual(iges_handler.extensions, ['.iges', '.igs']) - - def test_iges_parse_failing_filename_type(self): - iges_handler = ih.IgesHandler() - with self.assertRaises(TypeError): - mesh_points = iges_handler.parse(5.2) - - def test_iges_parse_failing_check_extension(self): - iges_handler = ih.IgesHandler() - with self.assertRaises(ValueError): - mesh_points = iges_handler.parse( - 'tests/test_datasets/test_pipe.vtk' - ) - - def test_iges_parse_infile(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') - self.assertEqual(iges_handler.infile, 'tests/test_datasets/test_pipe.iges') - - def test_iges_parse_control_point_position_member(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') - self.assertListEqual( - iges_handler._control_point_position, - [0, 6, 12, 18, 24, 28, 32] - ) - - def test_iges_parse_shape(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') - self.assertTupleEqual(mesh_points.shape, (32, 3)) - - def test_iges_parse_shape_igs(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.igs') - self.assertTupleEqual(mesh_points.shape, (32, 3)) - - def test_iges_parse_coords_1(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') - np.testing.assert_almost_equal(mesh_points[6][0], -1000.0) - - def test_iges_parse_coords_2(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') - np.testing.assert_almost_equal(mesh_points[8][1], 999.99999997448208) - - def test_iges_parse_coords_3(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') - np.testing.assert_almost_equal(mesh_points[30][2], 10000.0) - - def test_iges_parse_coords_4(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') - np.testing.assert_almost_equal(mesh_points[0][0], 0.0) - - def test_iges_parse_coords_5(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') - np.testing.assert_almost_equal(mesh_points[-1][2], 10000.0) - - def test_iges_parse_coords_5_igs(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.igs') - np.testing.assert_almost_equal(mesh_points[-1][2], 10000.0) - - def test_iges_write_failing_filename_type(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') - with self.assertRaises(TypeError): - iges_handler.write(mesh_points, -2) - - def test_iges_write_failing_check_extension(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') - with self.assertRaises(ValueError): - iges_handler.write( - mesh_points, 'tests/test_datasets/test_square.stl' - ) - - def test_iges_write_failing_infile_instantiation(self): - iges_handler = ih.IgesHandler() - mesh_points = np.zeros((20, 3)) - with self.assertRaises(RuntimeError): - iges_handler.write( - mesh_points, 'tests/test_datasets/test_pipe_out.iges' - ) - - def test_iges_write_outfile(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') - outfilename = 'tests/test_datasets/test_pipe_out.iges' - iges_handler.write(mesh_points, outfilename) - self.assertEqual(iges_handler.outfile, outfilename) - self.addCleanup(os.remove, outfilename) - - def test_iges_write_outfile_tolerance(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') - outfilename = 'tests/test_datasets/test_pipe_out.iges' - iges_handler.write(mesh_points, outfilename, 1e-3) - self.assertEqual(iges_handler.tolerance, 1e-3) - self.addCleanup(os.remove, outfilename) - - def test_iges_write_modified_tolerance(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') - outfilename = 'tests/test_datasets/test_pipe_out.iges' - iges_handler.write(mesh_points, outfilename, 1e-3) - self.assertEqual(iges_handler.outfile, outfilename) - self.addCleanup(os.remove, outfilename) - - def test_iges_write_comparison_iges(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') - mesh_points[0][0] = 2.2 - mesh_points[5][1] = 4.3 - mesh_points[9][2] = 0.5 - mesh_points[12][0] = 7.2 - mesh_points[16][1] = -1.2 - mesh_points[31][2] = -3.6 - - outfilename = 'tests/test_datasets/test_pipe_out.iges' - outfilename_expected = 'tests/test_datasets/test_pipe_out_true.iges' - - iges_handler.write(mesh_points, outfilename) - - mesh_points = iges_handler.parse(outfilename) - mesh_points_expected = iges_handler.parse(outfilename_expected) - np.testing.assert_array_almost_equal(mesh_points, mesh_points_expected) - self.addCleanup(os.remove, outfilename) - - def test_iges_write_comparison_igs(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.igs') - mesh_points[0][0] = 2.2 - mesh_points[5][1] = 4.3 - mesh_points[9][2] = 0.5 - mesh_points[12][0] = 7.2 - mesh_points[16][1] = -1.2 - mesh_points[31][2] = -3.6 - - outfilename = 'tests/test_datasets/test_pipe_out.igs' - outfilename_expected = 'tests/test_datasets/test_pipe_out_true.igs' - - iges_handler.write(mesh_points, outfilename) - - mesh_points = iges_handler.parse(outfilename) - mesh_points_expected = iges_handler.parse(outfilename_expected) - np.testing.assert_array_almost_equal(mesh_points, mesh_points_expected) - self.addCleanup(os.remove, outfilename) - - def test_iges_plot_save_fig(self): - iges_handler = ih.IgesHandler() - mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') - iges_handler.plot(save_fig=True) - self.assertTrue(os.path.isfile('tests/test_datasets/test_pipe.png')) - self.addCleanup(os.remove, 'tests/test_datasets/test_pipe.png') - - def test_iges_plot_failing_outfile_type(self): - iges_handler = ih.IgesHandler() - with self.assertRaises(TypeError): - iges_handler.plot(plot_file=3) - - def test_iges_ihow_failing_outfile_type(self): - iges_handler = ih.IgesHandler() - with self.assertRaises(TypeError): - iges_handler.show(ihow_file=1.1) - - def test_iges_load_shape_from_file_raises_wrong_type(self): - iges_handler = ih.IgesHandler() - with self.assertRaises(TypeError): - iges_handler.load_shape_from_file(None) - - def test_iges_load_shape_from_file_raises_wrong_extension(self): - iges_handler = ih.IgesHandler() - with self.assertRaises(ValueError): - iges_handler.load_shape_from_file( - 'tests/test_datasets/test_pipe.stp' - ) - - def test_iges_load_shape_correct_iges(self): - iges_handler = ih.IgesHandler() - shape = iges_handler.load_shape_from_file( - 'tests/test_datasets/test_pipe.iges' - ) - self.assertEqual(type(shape), TopoDS_Shape) - - def test_iges_load_shape_correct_igs(self): - iges_handler = ih.IgesHandler() - shape = iges_handler.load_shape_from_file( - 'tests/test_datasets/test_pipe.igs' - ) - self.assertEqual(type(shape), TopoDS_Shape) - - def test_iges_write_shape_to_file_raises_wrong_type(self): - iges_handler = ih.IgesHandler() - with self.assertRaises(TypeError): - iges_handler.write_shape_to_file(None, None) - - def test_iges_write_shape_to_file_raises_wrong_extension(self): - iges_handler = ih.IgesHandler() - with self.assertRaises(ValueError): - iges_handler.load_shape_from_file('tests/test_datasets/x.stp') - - def test_iges_write_shape_to_file_iges(self): - ihp = BRepPrimAPI_MakeBox(1., 1., 1.).Shape() - path = 'tests/test_datasets/x.iges' - iges_handler = ih.IgesHandler() - iges_handler.write_shape_to_file(ihp, path) - self.assertTrue(os.path.exists(path)) - self.addCleanup(os.remove, path) - - def test_iges_write_shape_to_file_igs(self): - ihp = BRepPrimAPI_MakeBox(1., 1., 1.).Shape() - path = 'tests/test_datasets/x.igs' - iges_handler = ih.IgesHandler() - iges_handler.write_shape_to_file(ihp, path) - self.assertTrue(os.path.exists(path)) - self.addCleanup(os.remove, path) + def test_iges_instantiation(self): + iges_handler = ih.IgesHandler() + + def test_iges_default_extension_member(self): + iges_handler = ih.IgesHandler() + self.assertListEqual(iges_handler.extensions, ['.iges', '.igs']) + + def test_iges_parse_failing_filename_type(self): + iges_handler = ih.IgesHandler() + with self.assertRaises(TypeError): + mesh_points = iges_handler.parse(5.2) + + def test_iges_parse_failing_check_extension(self): + iges_handler = ih.IgesHandler() + with self.assertRaises(ValueError): + mesh_points = iges_handler.parse( + 'tests/test_datasets/test_pipe.vtk') + + def test_iges_parse_infile(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') + self.assertEqual(iges_handler.infile, + 'tests/test_datasets/test_pipe.iges') + + def test_iges_parse_control_point_position_member(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') + self.assertListEqual(iges_handler._control_point_position, + [0, 6, 12, 18, 24, 28, 32]) + + def test_iges_parse_shape(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') + self.assertTupleEqual(mesh_points.shape, (32, 3)) + + def test_iges_parse_shape_igs(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.igs') + self.assertTupleEqual(mesh_points.shape, (32, 3)) + + def test_iges_parse_coords_1(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') + np.testing.assert_almost_equal(mesh_points[6][0], -1000.0) + + def test_iges_parse_coords_2(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') + np.testing.assert_almost_equal(mesh_points[8][1], 999.99999997448208) + + def test_iges_parse_coords_3(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') + np.testing.assert_almost_equal(mesh_points[30][2], 10000.0) + + def test_iges_parse_coords_4(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') + np.testing.assert_almost_equal(mesh_points[0][0], 0.0) + + def test_iges_parse_coords_5(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') + np.testing.assert_almost_equal(mesh_points[-1][2], 10000.0) + + def test_iges_parse_coords_5_igs(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.igs') + np.testing.assert_almost_equal(mesh_points[-1][2], 10000.0) + + def test_iges_write_failing_filename_type(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') + with self.assertRaises(TypeError): + iges_handler.write(mesh_points, -2) + + def test_iges_write_failing_check_extension(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') + with self.assertRaises(ValueError): + iges_handler.write(mesh_points, + 'tests/test_datasets/test_square.stl') + + def test_iges_write_failing_infile_instantiation(self): + iges_handler = ih.IgesHandler() + mesh_points = np.zeros((20, 3)) + with self.assertRaises(RuntimeError): + iges_handler.write(mesh_points, + 'tests/test_datasets/test_pipe_out.iges') + + def test_iges_write_outfile(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') + outfilename = 'tests/test_datasets/test_pipe_out.iges' + iges_handler.write(mesh_points, outfilename) + self.assertEqual(iges_handler.outfile, outfilename) + self.addCleanup(os.remove, outfilename) + + def test_iges_write_outfile_tolerance(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') + outfilename = 'tests/test_datasets/test_pipe_out.iges' + iges_handler.write(mesh_points, outfilename, 1e-3) + self.assertEqual(iges_handler.tolerance, 1e-3) + self.addCleanup(os.remove, outfilename) + + def test_iges_write_modified_tolerance(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') + outfilename = 'tests/test_datasets/test_pipe_out.iges' + iges_handler.write(mesh_points, outfilename, 1e-3) + self.assertEqual(iges_handler.outfile, outfilename) + self.addCleanup(os.remove, outfilename) + + def test_iges_write_comparison_iges(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') + mesh_points[0][0] = 2.2 + mesh_points[5][1] = 4.3 + mesh_points[9][2] = 0.5 + mesh_points[12][0] = 7.2 + mesh_points[16][1] = -1.2 + mesh_points[31][2] = -3.6 + + outfilename = 'tests/test_datasets/test_pipe_out.iges' + outfilename_expected = 'tests/test_datasets/test_pipe_out_true.iges' + + iges_handler.write(mesh_points, outfilename) + + mesh_points = iges_handler.parse(outfilename) + mesh_points_expected = iges_handler.parse(outfilename_expected) + np.testing.assert_array_almost_equal(mesh_points, mesh_points_expected) + self.addCleanup(os.remove, outfilename) + + def test_iges_write_comparison_igs(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.igs') + mesh_points[0][0] = 2.2 + mesh_points[5][1] = 4.3 + mesh_points[9][2] = 0.5 + mesh_points[12][0] = 7.2 + mesh_points[16][1] = -1.2 + mesh_points[31][2] = -3.6 + + outfilename = 'tests/test_datasets/test_pipe_out.igs' + outfilename_expected = 'tests/test_datasets/test_pipe_out_true.igs' + + iges_handler.write(mesh_points, outfilename) + + mesh_points = iges_handler.parse(outfilename) + mesh_points_expected = iges_handler.parse(outfilename_expected) + np.testing.assert_array_almost_equal(mesh_points, mesh_points_expected) + self.addCleanup(os.remove, outfilename) + + def test_iges_plot_save_fig(self): + iges_handler = ih.IgesHandler() + mesh_points = iges_handler.parse('tests/test_datasets/test_pipe.iges') + iges_handler.plot(save_fig=True) + self.assertTrue(os.path.isfile('tests/test_datasets/test_pipe.png')) + self.addCleanup(os.remove, 'tests/test_datasets/test_pipe.png') + + def test_iges_plot_failing_outfile_type(self): + iges_handler = ih.IgesHandler() + with self.assertRaises(TypeError): + iges_handler.plot(plot_file=3) + + def test_iges_ihow_failing_outfile_type(self): + iges_handler = ih.IgesHandler() + with self.assertRaises(TypeError): + iges_handler.show(ihow_file=1.1) + + def test_iges_load_shape_from_file_raises_wrong_type(self): + iges_handler = ih.IgesHandler() + with self.assertRaises(TypeError): + iges_handler.load_shape_from_file(None) + + def test_iges_load_shape_from_file_raises_wrong_extension(self): + iges_handler = ih.IgesHandler() + with self.assertRaises(ValueError): + iges_handler.load_shape_from_file( + 'tests/test_datasets/test_pipe.stp') + + def test_iges_load_shape_correct_iges(self): + iges_handler = ih.IgesHandler() + shape = iges_handler.load_shape_from_file( + 'tests/test_datasets/test_pipe.iges') + self.assertEqual(type(shape), TopoDS_Shape) + + def test_iges_load_shape_correct_igs(self): + iges_handler = ih.IgesHandler() + shape = iges_handler.load_shape_from_file( + 'tests/test_datasets/test_pipe.igs') + self.assertEqual(type(shape), TopoDS_Shape) + + def test_iges_write_shape_to_file_raises_wrong_type(self): + iges_handler = ih.IgesHandler() + with self.assertRaises(TypeError): + iges_handler.write_shape_to_file(None, None) + + def test_iges_write_shape_to_file_raises_wrong_extension(self): + iges_handler = ih.IgesHandler() + with self.assertRaises(ValueError): + iges_handler.load_shape_from_file('tests/test_datasets/x.stp') + + def test_iges_write_shape_to_file_iges(self): + ihp = BRepPrimAPI_MakeBox(1., 1., 1.).Shape() + path = 'tests/test_datasets/x.iges' + iges_handler = ih.IgesHandler() + iges_handler.write_shape_to_file(ihp, path) + self.assertTrue(os.path.exists(path)) + self.addCleanup(os.remove, path) + + def test_iges_write_shape_to_file_igs(self): + ihp = BRepPrimAPI_MakeBox(1., 1., 1.).Shape() + path = 'tests/test_datasets/x.igs' + iges_handler = ih.IgesHandler() + iges_handler.write_shape_to_file(ihp, path) + self.assertTrue(os.path.exists(path)) + self.addCleanup(os.remove, path) diff --git a/tests/test_khandler.py b/tests/test_khandler.py index a05b99c..8add01a 100644 --- a/tests/test_khandler.py +++ b/tests/test_khandler.py @@ -30,7 +30,7 @@ def test_k_parse_failing_check_extension(self): k_handler = uh.KHandler() with self.assertRaises(ValueError): mesh_points = k_handler.parse( - 'tests/test_datasets/test_square.iges') + 'tests/test_datasets/test_square.iges') def test_k_parse_infile(self): k_handler = uh.KHandler() @@ -77,15 +77,14 @@ def test_k_write_failing_check_extension(self): k_handler = uh.KHandler() mesh_points = k_handler.parse('tests/test_datasets/test_square.k') with self.assertRaises(ValueError): - k_handler.write(mesh_points, - 'tests/test_datasets/test_square.iges') + k_handler.write(mesh_points, 'tests/test_datasets/test_square.iges') def test_k_write_failing_infile_instantiation(self): k_handler = uh.KHandler() mesh_points = np.zeros((20, 3)) with self.assertRaises(RuntimeError): - k_handler.write(mesh_points, - 'tests/test_datasets/test_square_out.k') + k_handler.write(mesh_points, + 'tests/test_datasets/test_square_out.k') def test_k_write_outfile(self): k_handler = uh.KHandler() diff --git a/tests/test_nurbshandler.py b/tests/test_nurbshandler.py index 5332edc..82afa1f 100644 --- a/tests/test_nurbshandler.py +++ b/tests/test_nurbshandler.py @@ -4,67 +4,66 @@ class TestNurbsHandler(TestCase): - def test_nurbs_instantiation(self): - nurbs_handler = NurbsHandler() + def test_nurbs_instantiation(self): + nurbs_handler = NurbsHandler() - def test_nurbs_default_infile_member(self): - nurbs_handler = NurbsHandler() - self.assertIsNone(nurbs_handler.infile) + def test_nurbs_default_infile_member(self): + nurbs_handler = NurbsHandler() + self.assertIsNone(nurbs_handler.infile) - def test_nurbs_default_shape_member(self): - nurbs_handler = NurbsHandler() - self.assertIsNone(nurbs_handler.shape) + def test_nurbs_default_shape_member(self): + nurbs_handler = NurbsHandler() + self.assertIsNone(nurbs_handler.shape) - def test_nurbs_default_control_point_position_member(self): - nurbs_handler = NurbsHandler() - self.assertIsNone(nurbs_handler._control_point_position) + def test_nurbs_default_control_point_position_member(self): + nurbs_handler = NurbsHandler() + self.assertIsNone(nurbs_handler._control_point_position) - def test_nurbs_default_outfile_member(self): - nurbs_handler = NurbsHandler() - self.assertIsNone(nurbs_handler.outfile) + def test_nurbs_default_outfile_member(self): + nurbs_handler = NurbsHandler() + self.assertIsNone(nurbs_handler.outfile) - def test_nurbs_default_tolerance(self): - nurbs_handler = NurbsHandler() - self.assertAlmostEqual(nurbs_handler.tolerance, 1e-6) + def test_nurbs_default_tolerance(self): + nurbs_handler = NurbsHandler() + self.assertAlmostEqual(nurbs_handler.tolerance, 1e-6) - def test_nurbs_default_extension_member(self): - nurbs_handler = NurbsHandler() - self.assertListEqual(nurbs_handler.extensions, []) + def test_nurbs_default_extension_member(self): + nurbs_handler = NurbsHandler() + self.assertListEqual(nurbs_handler.extensions, []) - def test_nurbs_load_shape_from_file_raises(self): - nurbs_handler = NurbsHandler() - with self.assertRaises(NotImplementedError): - nurbs_handler.load_shape_from_file(None) + def test_nurbs_load_shape_from_file_raises(self): + nurbs_handler = NurbsHandler() + with self.assertRaises(NotImplementedError): + nurbs_handler.load_shape_from_file(None) - def test_nurbs_write_shape_to_file_raises(self): - nurbs_handler = NurbsHandler() - with self.assertRaises(NotImplementedError): - nurbs_handler.write_shape_to_file(None, None) + def test_nurbs_write_shape_to_file_raises(self): + nurbs_handler = NurbsHandler() + with self.assertRaises(NotImplementedError): + nurbs_handler.write_shape_to_file(None, None) - def test_nurbs_check_infile_instantiation_no_shape(self): - nurbs_handler = NurbsHandler() - nurbs_handler.infile = "something" - with self.assertRaises(RuntimeError): - nurbs_handler._check_infile_instantiation() + def test_nurbs_check_infile_instantiation_no_shape(self): + nurbs_handler = NurbsHandler() + nurbs_handler.infile = "something" + with self.assertRaises(RuntimeError): + nurbs_handler._check_infile_instantiation() - def test_nurbs_check_infile_instantiation_no_infile(self): - nurbs_handler = NurbsHandler() - nurbs_handler.shape = True - with self.assertRaises(RuntimeError): - nurbs_handler._check_infile_instantiation() + def test_nurbs_check_infile_instantiation_no_infile(self): + nurbs_handler = NurbsHandler() + nurbs_handler.shape = True + with self.assertRaises(RuntimeError): + nurbs_handler._check_infile_instantiation() - def test_nurbs_check_infile_instantiation_shape_infile_wrong(self): - nurbs_handler = NurbsHandler() - with self.assertRaises(RuntimeError): - nurbs_handler._check_infile_instantiation() + def test_nurbs_check_infile_instantiation_shape_infile_wrong(self): + nurbs_handler = NurbsHandler() + with self.assertRaises(RuntimeError): + nurbs_handler._check_infile_instantiation() - def test_nurbs_check_infile_instantiation_correct(self): - nurbs_handler = NurbsHandler() - nurbs_handler.shape = True - nurbs_handler.infile = "something" - try: - nurbs_handler._check_infile_instantiation() - except RuntimeError: - self.fail( - "Handler was instantiated correctly, yet an error was raised." - ) + def test_nurbs_check_infile_instantiation_correct(self): + nurbs_handler = NurbsHandler() + nurbs_handler.shape = True + nurbs_handler.infile = "something" + try: + nurbs_handler._check_infile_instantiation() + except RuntimeError: + self.fail( + "Handler was instantiated correctly, yet an error was raised.") diff --git a/tests/test_openfhandler.py b/tests/test_openfhandler.py index b0a9dd6..1ee4448 100644 --- a/tests/test_openfhandler.py +++ b/tests/test_openfhandler.py @@ -7,136 +7,123 @@ class TestOpenFoamHandler(TestCase): - def test_open_foam_instantiation(self): - open_foam_handler = ofh.OpenFoamHandler() - - def test_open_foam_default_infile_member(self): - open_foam_handler = ofh.OpenFoamHandler() - self.assertIsNone(open_foam_handler.infile) - - def test_open_foam_default_outfile_member(self): - open_foam_handler = ofh.OpenFoamHandler() - self.assertIsNone(open_foam_handler.outfile) - - def test_open_foam_default_extension_member(self): - open_foam_handler = ofh.OpenFoamHandler() - self.assertListEqual(open_foam_handler.extensions, ['']) - - def test_open_foam_parse_failing_filename_type(self): - open_foam_handler = ofh.OpenFoamHandler() - with self.assertRaises(TypeError): - mesh_points = open_foam_handler.parse(.2) - - def test_open_foam_parse_failing_check_extension(self): - open_foam_handler = ofh.OpenFoamHandler() - with self.assertRaises(ValueError): - mesh_points = open_foam_handler.parse( - 'tests/test_datasets/test_square.iges' - ) - - def test_open_foam_parse_infile(self): - open_foam_handler = ofh.OpenFoamHandler() - mesh_points = open_foam_handler.parse( - 'tests/test_datasets/test_openFOAM' - ) - self.assertEqual(open_foam_handler.infile, 'tests/test_datasets/test_openFOAM') - - def test_open_foam_parse_shape(self): - open_foam_handler = ofh.OpenFoamHandler() - mesh_points = open_foam_handler.parse( - 'tests/test_datasets/test_openFOAM' - ) - self.assertTupleEqual(mesh_points.shape, (21812, 3)) - - def test_open_foam_parse_coords_1(self): - open_foam_handler = ofh.OpenFoamHandler() - mesh_points = open_foam_handler.parse( - 'tests/test_datasets/test_openFOAM' - ) - np.testing.assert_almost_equal(mesh_points[33][0], 1.42254) - - def test_open_foam_parse_coords_2(self): - open_foam_handler = ofh.OpenFoamHandler() - mesh_points = open_foam_handler.parse( - 'tests/test_datasets/test_openFOAM' - ) - np.testing.assert_almost_equal(mesh_points[1708][1], -3.13059) - - def test_open_foam_parse_coords_3(self): - open_foam_handler = ofh.OpenFoamHandler() - mesh_points = open_foam_handler.parse( - 'tests/test_datasets/test_openFOAM' - ) - np.testing.assert_almost_equal(mesh_points[3527][2], .0) - - def test_open_foam_parse_coords_4(self): - open_foam_handler = ofh.OpenFoamHandler() - mesh_points = open_foam_handler.parse( - 'tests/test_datasets/test_openFOAM' - ) - np.testing.assert_almost_equal(mesh_points[0][0], -17.5492) - - def test_open_foam_parse_coords_5(self): - open_foam_handler = ofh.OpenFoamHandler() - mesh_points = open_foam_handler.parse( - 'tests/test_datasets/test_openFOAM' - ) - np.testing.assert_almost_equal(mesh_points[-1][2], 0.05) - - def test_open_foam_write_failing_filename_type(self): - open_foam_handler = ofh.OpenFoamHandler() - mesh_points = open_foam_handler.parse( - 'tests/test_datasets/test_openFOAM' - ) - with self.assertRaises(TypeError): - open_foam_handler.write(mesh_points, -1.) - - def test_open_foam_write_failing_check_extension(self): - open_foam_handler = ofh.OpenFoamHandler() - mesh_points = open_foam_handler.parse( - 'tests/test_datasets/test_openFOAM' - ) - with self.assertRaises(ValueError): - open_foam_handler.write( - mesh_points, 'tests/test_datasets/test_square.iges' - ) - - def test_open_foam_write_failing_infile_instantiation(self): - open_foam_handler = ofh.OpenFoamHandler() - mesh_points = np.zeros((40, 3)) - with self.assertRaises(RuntimeError): - open_foam_handler.write( - mesh_points, 'tests/test_datasets/test_openFOAM_out' - ) - - def test_open_foam_write_outfile(self): - open_foam_handler = ofh.OpenFoamHandler() - mesh_points = open_foam_handler.parse( - 'tests/test_datasets/test_openFOAM' - ) - outfilename = 'tests/test_datasets/test_openFOAM_out' - open_foam_handler.write(mesh_points, outfilename) - self.assertEqual(open_foam_handler.outfile, outfilename) - self.addCleanup(os.remove, outfilename) - - def test_open_foam_write_comparison(self): - open_foam_handler = ofh.OpenFoamHandler() - mesh_points = open_foam_handler.parse( - 'tests/test_datasets/test_openFOAM' - ) - mesh_points[0] = [-14., 1.55, 0.2] - mesh_points[1] = [-14.3, 2.55, 0.3] - mesh_points[2] = [-14.3, 2.55, 0.3] - mesh_points[2000] = [7.8, -42.8, .0] - mesh_points[2001] = [8.8, -41.8, .1] - mesh_points[2002] = [9.8, -40.8, .0] - mesh_points[-3] = [236.3, 183.7, 0.06] - mesh_points[-2] = [237.3, 183.7, 0.06] - mesh_points[-1] = [236.3, 185.7, 0.06] - - outfilename = 'tests/test_datasets/test_openFOAM_out' - outfilename_expected = 'tests/test_datasets/test_openFOAM_out_true' - - open_foam_handler.write(mesh_points, outfilename) - self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) - self.addCleanup(os.remove, outfilename) + def test_open_foam_instantiation(self): + open_foam_handler = ofh.OpenFoamHandler() + + def test_open_foam_default_infile_member(self): + open_foam_handler = ofh.OpenFoamHandler() + self.assertIsNone(open_foam_handler.infile) + + def test_open_foam_default_outfile_member(self): + open_foam_handler = ofh.OpenFoamHandler() + self.assertIsNone(open_foam_handler.outfile) + + def test_open_foam_default_extension_member(self): + open_foam_handler = ofh.OpenFoamHandler() + self.assertListEqual(open_foam_handler.extensions, ['']) + + def test_open_foam_parse_failing_filename_type(self): + open_foam_handler = ofh.OpenFoamHandler() + with self.assertRaises(TypeError): + mesh_points = open_foam_handler.parse(.2) + + def test_open_foam_parse_failing_check_extension(self): + open_foam_handler = ofh.OpenFoamHandler() + with self.assertRaises(ValueError): + mesh_points = open_foam_handler.parse( + 'tests/test_datasets/test_square.iges') + + def test_open_foam_parse_infile(self): + open_foam_handler = ofh.OpenFoamHandler() + mesh_points = open_foam_handler.parse( + 'tests/test_datasets/test_openFOAM') + self.assertEqual(open_foam_handler.infile, + 'tests/test_datasets/test_openFOAM') + + def test_open_foam_parse_shape(self): + open_foam_handler = ofh.OpenFoamHandler() + mesh_points = open_foam_handler.parse( + 'tests/test_datasets/test_openFOAM') + self.assertTupleEqual(mesh_points.shape, (21812, 3)) + + def test_open_foam_parse_coords_1(self): + open_foam_handler = ofh.OpenFoamHandler() + mesh_points = open_foam_handler.parse( + 'tests/test_datasets/test_openFOAM') + np.testing.assert_almost_equal(mesh_points[33][0], 1.42254) + + def test_open_foam_parse_coords_2(self): + open_foam_handler = ofh.OpenFoamHandler() + mesh_points = open_foam_handler.parse( + 'tests/test_datasets/test_openFOAM') + np.testing.assert_almost_equal(mesh_points[1708][1], -3.13059) + + def test_open_foam_parse_coords_3(self): + open_foam_handler = ofh.OpenFoamHandler() + mesh_points = open_foam_handler.parse( + 'tests/test_datasets/test_openFOAM') + np.testing.assert_almost_equal(mesh_points[3527][2], .0) + + def test_open_foam_parse_coords_4(self): + open_foam_handler = ofh.OpenFoamHandler() + mesh_points = open_foam_handler.parse( + 'tests/test_datasets/test_openFOAM') + np.testing.assert_almost_equal(mesh_points[0][0], -17.5492) + + def test_open_foam_parse_coords_5(self): + open_foam_handler = ofh.OpenFoamHandler() + mesh_points = open_foam_handler.parse( + 'tests/test_datasets/test_openFOAM') + np.testing.assert_almost_equal(mesh_points[-1][2], 0.05) + + def test_open_foam_write_failing_filename_type(self): + open_foam_handler = ofh.OpenFoamHandler() + mesh_points = open_foam_handler.parse( + 'tests/test_datasets/test_openFOAM') + with self.assertRaises(TypeError): + open_foam_handler.write(mesh_points, -1.) + + def test_open_foam_write_failing_check_extension(self): + open_foam_handler = ofh.OpenFoamHandler() + mesh_points = open_foam_handler.parse( + 'tests/test_datasets/test_openFOAM') + with self.assertRaises(ValueError): + open_foam_handler.write(mesh_points, + 'tests/test_datasets/test_square.iges') + + def test_open_foam_write_failing_infile_instantiation(self): + open_foam_handler = ofh.OpenFoamHandler() + mesh_points = np.zeros((40, 3)) + with self.assertRaises(RuntimeError): + open_foam_handler.write(mesh_points, + 'tests/test_datasets/test_openFOAM_out') + + def test_open_foam_write_outfile(self): + open_foam_handler = ofh.OpenFoamHandler() + mesh_points = open_foam_handler.parse( + 'tests/test_datasets/test_openFOAM') + outfilename = 'tests/test_datasets/test_openFOAM_out' + open_foam_handler.write(mesh_points, outfilename) + self.assertEqual(open_foam_handler.outfile, outfilename) + self.addCleanup(os.remove, outfilename) + + def test_open_foam_write_comparison(self): + open_foam_handler = ofh.OpenFoamHandler() + mesh_points = open_foam_handler.parse( + 'tests/test_datasets/test_openFOAM') + mesh_points[0] = [-14., 1.55, 0.2] + mesh_points[1] = [-14.3, 2.55, 0.3] + mesh_points[2] = [-14.3, 2.55, 0.3] + mesh_points[2000] = [7.8, -42.8, .0] + mesh_points[2001] = [8.8, -41.8, .1] + mesh_points[2002] = [9.8, -40.8, .0] + mesh_points[-3] = [236.3, 183.7, 0.06] + mesh_points[-2] = [237.3, 183.7, 0.06] + mesh_points[-1] = [236.3, 185.7, 0.06] + + outfilename = 'tests/test_datasets/test_openFOAM_out' + outfilename_expected = 'tests/test_datasets/test_openFOAM_out_true' + + open_foam_handler.write(mesh_points, outfilename) + self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) + self.addCleanup(os.remove, outfilename) diff --git a/tests/test_package.py b/tests/test_package.py index 57b2ca4..6310f87 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -6,63 +6,63 @@ class TestPackage(TestCase): - def test_import_pg_1(self): - import pygem as pg - params = pg.params.FFDParameters() - - def test_import_pg_2(self): - import pygem as pg - mat = pg.affine.angles2matrix(2) - - def test_import_pg_3(self): - import pygem as pg - fh = pg.filehandler.FileHandler() - - def test_import_pg_4(self): - import pygem as pg - unvh = pg.unvhandler.UnvHandler() - - def test_import_pg_5(self): - import pygem as pg - stlh = pg.stlhandler.StlHandler() - - def test_import_pg_6(self): - import pygem as pg - opfh = pg.openfhandler.OpenFoamHandler() - - def test_import_pg_7(self): - import pygem as pg - vtkh = pg.vtkhandler.VtkHandler() - - def test_import_pg_8(self): - import pygem as pg - igesh = pg.igeshandler.IgesHandler() - - def test_import_pg_9(self): - import pygem as pg - guih = pg.gui.Gui() - - def test_import_pg_10(self): - import pygem as pg - nurh = pg.nurbshandler.NurbsHandler() - - def test_import_pg_11(self): - import pygem as pg - stph = pg.stephandler.StepHandler() - - def test_modules_name(self): - # it checks that __all__ includes all the .py files in pygem folder - import pygem - package = pygem - - f_aux = [] - for (__, __, filenames) in walk('pygem'): - f_aux.extend(filenames) - - f = [] - for i in f_aux: - file_name, file_ext = path.splitext(i) - if file_name != '__init__' and file_ext == '.py': - f.append(file_name) - - assert (sorted(package.__all__) == sorted(f)) + def test_import_pg_1(self): + import pygem as pg + params = pg.params.FFDParameters() + + def test_import_pg_2(self): + import pygem as pg + mat = pg.affine.angles2matrix(2) + + def test_import_pg_3(self): + import pygem as pg + fh = pg.filehandler.FileHandler() + + def test_import_pg_4(self): + import pygem as pg + unvh = pg.unvhandler.UnvHandler() + + def test_import_pg_5(self): + import pygem as pg + stlh = pg.stlhandler.StlHandler() + + def test_import_pg_6(self): + import pygem as pg + opfh = pg.openfhandler.OpenFoamHandler() + + def test_import_pg_7(self): + import pygem as pg + vtkh = pg.vtkhandler.VtkHandler() + + def test_import_pg_8(self): + import pygem as pg + igesh = pg.igeshandler.IgesHandler() + + def test_import_pg_9(self): + import pygem as pg + guih = pg.gui.Gui() + + def test_import_pg_10(self): + import pygem as pg + nurh = pg.nurbshandler.NurbsHandler() + + def test_import_pg_11(self): + import pygem as pg + stph = pg.stephandler.StepHandler() + + def test_modules_name(self): + # it checks that __all__ includes all the .py files in pygem folder + import pygem + package = pygem + + f_aux = [] + for (__, __, filenames) in walk('pygem'): + f_aux.extend(filenames) + + f = [] + for i in f_aux: + file_name, file_ext = path.splitext(i) + if file_name != '__init__' and file_ext == '.py': + f.append(file_name) + + assert (sorted(package.__all__) == sorted(f)) diff --git a/tests/test_radial.py b/tests/test_radial.py index 487481c..5b8ffcb 100644 --- a/tests/test_radial.py +++ b/tests/test_radial.py @@ -9,183 +9,155 @@ class TestRadial(TestCase): - def get_cube_mesh_points(self): - # Points that define a cube - nx, ny, nz = (20, 20, 20) - mesh = np.zeros((nx * ny * nz, 3)) - xv = np.linspace(0, 1, nx) - yv = np.linspace(0, 1, ny) - zv = np.linspace(0, 1, nz) - z, y, x = np.meshgrid(zv, yv, xv) - mesh = np.array([x.ravel(), y.ravel(), z.ravel()]) - original_mesh_points = mesh.T - return original_mesh_points - - def test_rbf_parameters_member(self): - params = rbfp.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_default.prm' - ) - rbf = rad.RBF(params, self.get_cube_mesh_points()) - assert rbf.parameters == params - - def test_rbf_original_mesh_points_member(self): - params = rbfp.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_default.prm' - ) - rbf = rad.RBF(params, self.get_cube_mesh_points()) - np.testing.assert_array_almost_equal( - rbf.original_mesh_points, self.get_cube_mesh_points() - ) - - def test_rbf_default_modified_mesh_points_member(self): - params = rbfp.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_default.prm' - ) - rbf = rad.RBF(params, self.get_cube_mesh_points()) - assert rbf.modified_mesh_points == None - - def test_rbf_modified_mesh_points_member(self): - params = rbfp.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_default.prm' - ) - rbf = rad.RBF(params, self.get_cube_mesh_points()) - rbf.perform() - np.testing.assert_array_almost_equal( - rbf.modified_mesh_points, self.get_cube_mesh_points() - ) - - def test_rbf_weights_member(self): - params = rbfp.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_cube.prm' - ) - rbf = rad.RBF(params, self.get_cube_mesh_points()) - weights_true = np.load('tests/test_datasets/weights_rbf_cube.npy') - np.testing.assert_array_almost_equal(rbf.weights, weights_true) - - def test_rbf_cube_mod(self): - params = rbfp.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_cube.prm' - ) - mesh_points_ref = np.load( - 'tests/test_datasets/meshpoints_cube_mod_rbf.npy' - ) - rbf = rad.RBF(params, self.get_cube_mesh_points()) - rbf.perform() - mesh_points_test = rbf.modified_mesh_points - np.testing.assert_array_almost_equal(mesh_points_test, mesh_points_ref) - - def test_wrong_basis(self): - params = rbfp.RBFParameters() - params.read_parameters( - 'tests/test_datasets/parameters_rbf_bugged_02.prm' - ) - with self.assertRaises(NameError): - rbf = rad.RBF(params, self.get_cube_mesh_points()) - - def test_gaussian_spline(self): - params = rbfp.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_default.prm' - ) - rbf = rad.RBF(params, self.get_cube_mesh_points()) - value = rbf.gaussian_spline( - np.array([0.5, 1, 2, 0.2]).reshape(4, 1), 0.2 - ) - np.testing.assert_almost_equal(value, 0.0) - - def test_multi_quadratic_biharmonic_spline(self): - params = rbfp.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_default.prm' - ) - rbf = rad.RBF(params, self.get_cube_mesh_points()) - value = rbf.multi_quadratic_biharmonic_spline( - np.array([0.5, 1, 2, 0.2]).reshape(4, 1), 0.2 - ) - np.testing.assert_almost_equal(value, 2.30867927612) - - def test_inv_multi_quadratic_biharmonic_spline(self): - params = rbfp.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_default.prm' - ) - rbf = rad.RBF(params, self.get_cube_mesh_points()) - value = rbf.inv_multi_quadratic_biharmonic_spline( - np.array([0.5, 1, 2, 0.2]).reshape(4, 1), 0.2 - ) - np.testing.assert_almost_equal(value, 0.433148081824) - - def test_thin_plate_spline(self): - params = rbfp.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_default.prm' - ) - rbf = rad.RBF(params, self.get_cube_mesh_points()) - value = rbf.thin_plate_spline( - np.array([0.5, 1, 2, 0.2]).reshape(4, 1), 0.2 - ) - np.testing.assert_almost_equal(value, 323.000395428) - - def test_beckert_wendland_c2_basis_01(self): - params = rbfp.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_default.prm' - ) - rbf = rad.RBF(params, self.get_cube_mesh_points()) - value = rbf.beckert_wendland_c2_basis( - np.array([0.5, 1, 2, 0.2]).reshape(4, 1), 0.2 - ) - np.testing.assert_almost_equal(value, 0.0) - - def test_beckert_wendland_c2_basis_02(self): - params = rbfp.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_default.prm' - ) - rbf = rad.RBF(params, self.get_cube_mesh_points()) - value = rbf.beckert_wendland_c2_basis( - np.array([0.1, 0.15, -0.2]).reshape(3, 1), 0.9 - ) - np.testing.assert_almost_equal(value, 0.529916819595) - - def test_polyharmonic_spline_k_even(self): - params = rbfp.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_default.prm' - ) - params.power = 3 - rbf = rad.RBF(params, self.get_cube_mesh_points()) - value = rbf.polyharmonic_spline( - np.array([0.1, 0.15, -0.2]).reshape(3, 1), 0.9 - ) - np.testing.assert_almost_equal(value, 0.02677808) - - def test_polyharmonic_spline_k_odd1(self): - params = rbfp.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_default.prm' - ) - params.power = 2 - rbf = rad.RBF(params, self.get_cube_mesh_points()) - value = rbf.polyharmonic_spline( - np.array([0.1, 0.15, -0.2]).reshape(3, 1), 0.9 - ) - np.testing.assert_almost_equal(value, -0.1080092) - - def test_polyharmonic_spline_k_odd2(self): - params = rbfp.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_default.prm' - ) - params.power = 2 - rbf = rad.RBF(params, self.get_cube_mesh_points()) - value = rbf.polyharmonic_spline( - np.array([0.1, 0.15, -0.2]).reshape(3, 1), 0.2 - ) - np.testing.assert_almost_equal(value, 0.53895331) + def get_cube_mesh_points(self): + # Points that define a cube + nx, ny, nz = (20, 20, 20) + mesh = np.zeros((nx * ny * nz, 3)) + xv = np.linspace(0, 1, nx) + yv = np.linspace(0, 1, ny) + zv = np.linspace(0, 1, nz) + z, y, x = np.meshgrid(zv, yv, xv) + mesh = np.array([x.ravel(), y.ravel(), z.ravel()]) + original_mesh_points = mesh.T + return original_mesh_points + + def test_rbf_parameters_member(self): + params = rbfp.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_default.prm') + rbf = rad.RBF(params, self.get_cube_mesh_points()) + assert rbf.parameters == params + + def test_rbf_original_mesh_points_member(self): + params = rbfp.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_default.prm') + rbf = rad.RBF(params, self.get_cube_mesh_points()) + np.testing.assert_array_almost_equal(rbf.original_mesh_points, + self.get_cube_mesh_points()) + + def test_rbf_default_modified_mesh_points_member(self): + params = rbfp.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_default.prm') + rbf = rad.RBF(params, self.get_cube_mesh_points()) + assert rbf.modified_mesh_points == None + + def test_rbf_modified_mesh_points_member(self): + params = rbfp.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_default.prm') + rbf = rad.RBF(params, self.get_cube_mesh_points()) + rbf.perform() + np.testing.assert_array_almost_equal(rbf.modified_mesh_points, + self.get_cube_mesh_points()) + + def test_rbf_weights_member(self): + params = rbfp.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_cube.prm') + rbf = rad.RBF(params, self.get_cube_mesh_points()) + weights_true = np.load('tests/test_datasets/weights_rbf_cube.npy') + np.testing.assert_array_almost_equal(rbf.weights, weights_true) + + def test_rbf_cube_mod(self): + params = rbfp.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_cube.prm') + mesh_points_ref = np.load( + 'tests/test_datasets/meshpoints_cube_mod_rbf.npy') + rbf = rad.RBF(params, self.get_cube_mesh_points()) + rbf.perform() + mesh_points_test = rbf.modified_mesh_points + np.testing.assert_array_almost_equal(mesh_points_test, mesh_points_ref) + + def test_wrong_basis(self): + params = rbfp.RBFParameters() + params.read_parameters( + 'tests/test_datasets/parameters_rbf_bugged_02.prm') + with self.assertRaises(NameError): + rbf = rad.RBF(params, self.get_cube_mesh_points()) + + def test_gaussian_spline(self): + params = rbfp.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_default.prm') + rbf = rad.RBF(params, self.get_cube_mesh_points()) + value = rbf.gaussian_spline( + np.array([0.5, 1, 2, 0.2]).reshape(4, 1), 0.2) + np.testing.assert_almost_equal(value, 0.0) + + def test_multi_quadratic_biharmonic_spline(self): + params = rbfp.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_default.prm') + rbf = rad.RBF(params, self.get_cube_mesh_points()) + value = rbf.multi_quadratic_biharmonic_spline( + np.array([0.5, 1, 2, 0.2]).reshape(4, 1), 0.2) + np.testing.assert_almost_equal(value, 2.30867927612) + + def test_inv_multi_quadratic_biharmonic_spline(self): + params = rbfp.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_default.prm') + rbf = rad.RBF(params, self.get_cube_mesh_points()) + value = rbf.inv_multi_quadratic_biharmonic_spline( + np.array([0.5, 1, 2, 0.2]).reshape(4, 1), 0.2) + np.testing.assert_almost_equal(value, 0.433148081824) + + def test_thin_plate_spline(self): + params = rbfp.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_default.prm') + rbf = rad.RBF(params, self.get_cube_mesh_points()) + value = rbf.thin_plate_spline( + np.array([0.5, 1, 2, 0.2]).reshape(4, 1), 0.2) + np.testing.assert_almost_equal(value, 323.000395428) + + def test_beckert_wendland_c2_basis_01(self): + params = rbfp.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_default.prm') + rbf = rad.RBF(params, self.get_cube_mesh_points()) + value = rbf.beckert_wendland_c2_basis( + np.array([0.5, 1, 2, 0.2]).reshape(4, 1), 0.2) + np.testing.assert_almost_equal(value, 0.0) + + def test_beckert_wendland_c2_basis_02(self): + params = rbfp.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_default.prm') + rbf = rad.RBF(params, self.get_cube_mesh_points()) + value = rbf.beckert_wendland_c2_basis( + np.array([0.1, 0.15, -0.2]).reshape(3, 1), 0.9) + np.testing.assert_almost_equal(value, 0.529916819595) + + def test_polyharmonic_spline_k_even(self): + params = rbfp.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_default.prm') + params.power = 3 + rbf = rad.RBF(params, self.get_cube_mesh_points()) + value = rbf.polyharmonic_spline( + np.array([0.1, 0.15, -0.2]).reshape(3, 1), 0.9) + np.testing.assert_almost_equal(value, 0.02677808) + + def test_polyharmonic_spline_k_odd1(self): + params = rbfp.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_default.prm') + params.power = 2 + rbf = rad.RBF(params, self.get_cube_mesh_points()) + value = rbf.polyharmonic_spline( + np.array([0.1, 0.15, -0.2]).reshape(3, 1), 0.9) + np.testing.assert_almost_equal(value, -0.1080092) + + def test_polyharmonic_spline_k_odd2(self): + params = rbfp.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_default.prm') + params.power = 2 + rbf = rad.RBF(params, self.get_cube_mesh_points()) + value = rbf.polyharmonic_spline( + np.array([0.1, 0.15, -0.2]).reshape(3, 1), 0.2) + np.testing.assert_almost_equal(value, 0.53895331) diff --git a/tests/test_rbfparams.py b/tests/test_rbfparams.py index fe1b098..e5acb52 100644 --- a/tests/test_rbfparams.py +++ b/tests/test_rbfparams.py @@ -7,130 +7,125 @@ class TestRBFParameters(TestCase): - def test_class_members_default_basis(self): - params = rbfp.RBFParameters() - assert params.basis == None - - def test_class_members_default_radius(self): - params = rbfp.RBFParameters() - assert params.radius == None - - def test_class_members_default_n_control_points(self): - params = rbfp.RBFParameters() - assert params.n_control_points == None - - def test_class_members_default_original_control_points(self): - params = rbfp.RBFParameters() - assert params.original_control_points == None - - def test_class_members_default_deformed_control_points(self): - params = rbfp.RBFParameters() - assert params.deformed_control_points == None - - def test_read_parameters_basis(self): - params = rbfp.RBFParameters() - params.read_parameters('tests/test_datasets/parameters_rbf_default.prm') - assert params.basis == 'gaussian_spline' - - def test_read_parameters_radius(self): - params = rbfp.RBFParameters() - params.read_parameters('tests/test_datasets/parameters_rbf_default.prm') - assert params.radius == 0.5 - - def test_read_parameters_n_control_points(self): - params = rbfp.RBFParameters() - params.read_parameters('tests/test_datasets/parameters_rbf_default.prm') - assert params.n_control_points == 8 - - def test_read_parameters_original_control_points(self): - params = rbfp.RBFParameters() - params.read_parameters('tests/test_datasets/parameters_rbf_default.prm') - original_control_points_exact = np.array([0., 0., 0., 0., 0., 1., 0., 1., 0., 1., 0., 0., \ - 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1., 1.]).reshape((8, 3)) - np.testing.assert_array_almost_equal( - params.original_control_points, original_control_points_exact - ) - - def test_read_parameters_deformed_control_points(self): - params = rbfp.RBFParameters() - params.read_parameters('tests/test_datasets/parameters_rbf_default.prm') - deformed_control_points_exact = np.array([0., 0., 0., 0., 0., 1., 0., 1., 0., 1., 0., 0., \ - 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1., 1.]).reshape((8, 3)) - np.testing.assert_array_almost_equal( - params.deformed_control_points, deformed_control_points_exact - ) - - def test_read_parameters_failing_filename_type(self): - params = rbfp.RBFParameters() - with self.assertRaises(TypeError): - params.read_parameters(3) - - def test_read_parameters_failing_number_deformed_control_points(self): - params = rbfp.RBFParameters() - with self.assertRaises(TypeError): - params.read_parameters( - 'tests/test_datasets/parameters_rbf_bugged_01.prm' - ) - - def test_write_parameters_failing_filename_type(self): - params = rbfp.RBFParameters() - with self.assertRaises(TypeError): - params.write_parameters(5) - - def test_write_parameters_filename_default_existance(self): - params = rbfp.RBFParameters() - params.basis = 'inv_multi_quadratic_biharmonic_spline' - params.radius = 0.1 - params.n_control_points = 3 - params.original_control_points = np.array( - [0., 0., 0., 0., 0., 1., 0., 1., 0.] - ).reshape((3, 3)) - params.deformed_control_points = np.array( - [0., 0., 0., 0., 0., 1., 0., 1., 0.] - ).reshape((3, 3)) - params.write_parameters() - outfilename = 'parameters_rbf.prm' - assert os.path.isfile(outfilename) - os.remove(outfilename) - - def test_write_parameters_filename_default(self): - params = rbfp.RBFParameters() - params.basis = 'gaussian_spline' - params.radius = 0.5 - params.n_control_points = 8 - params.power = 2 - params.original_control_points = np.array([0., 0., 0., 0., 0., 1., 0., 1., 0., 1., 0., 0., \ - 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1., 1.]).reshape((8, 3)) - params.deformed_control_points = np.array([0., 0., 0., 0., 0., 1., 0., 1., 0., 1., 0., 0., \ - 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1., 1.]).reshape((8, 3)) - outfilename = 'test.prm' - params.write_parameters(outfilename) - outfilename_expected = 'tests/test_datasets/parameters_rbf_default.prm' - - print(filecmp.cmp(outfilename, outfilename_expected)) - self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) - os.remove(outfilename) - - def test_write_parameters(self): - params = rbfp.RBFParameters() - params.read_parameters('tests/test_datasets/parameters_rbf_cube.prm') - - outfilename = 'tests/test_datasets/parameters_rbf_cube_out.prm' - outfilename_expected = 'tests/test_datasets/parameters_rbf_cube_out_true.prm' - params.write_parameters(outfilename) - - self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) - os.remove(outfilename) - - def test_read_parameters_filename_default(self): - params = rbfp.RBFParameters() - params.read_parameters() - outfilename = 'parameters_rbf.prm' - outfilename_expected = 'tests/test_datasets/parameters_rbf_default.prm' - - self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) - os.remove(outfilename) - - def test_print_info(self): - params = rbfp.RBFParameters() - print(params) + def test_class_members_default_basis(self): + params = rbfp.RBFParameters() + assert params.basis == None + + def test_class_members_default_radius(self): + params = rbfp.RBFParameters() + assert params.radius == None + + def test_class_members_default_n_control_points(self): + params = rbfp.RBFParameters() + assert params.n_control_points == None + + def test_class_members_default_original_control_points(self): + params = rbfp.RBFParameters() + assert params.original_control_points == None + + def test_class_members_default_deformed_control_points(self): + params = rbfp.RBFParameters() + assert params.deformed_control_points == None + + def test_read_parameters_basis(self): + params = rbfp.RBFParameters() + params.read_parameters('tests/test_datasets/parameters_rbf_default.prm') + assert params.basis == 'gaussian_spline' + + def test_read_parameters_radius(self): + params = rbfp.RBFParameters() + params.read_parameters('tests/test_datasets/parameters_rbf_default.prm') + assert params.radius == 0.5 + + def test_read_parameters_n_control_points(self): + params = rbfp.RBFParameters() + params.read_parameters('tests/test_datasets/parameters_rbf_default.prm') + assert params.n_control_points == 8 + + def test_read_parameters_original_control_points(self): + params = rbfp.RBFParameters() + params.read_parameters('tests/test_datasets/parameters_rbf_default.prm') + original_control_points_exact = np.array([0., 0., 0., 0., 0., 1., 0., 1., 0., 1., 0., 0., \ + 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1., 1.]).reshape((8, 3)) + np.testing.assert_array_almost_equal(params.original_control_points, + original_control_points_exact) + + def test_read_parameters_deformed_control_points(self): + params = rbfp.RBFParameters() + params.read_parameters('tests/test_datasets/parameters_rbf_default.prm') + deformed_control_points_exact = np.array([0., 0., 0., 0., 0., 1., 0., 1., 0., 1., 0., 0., \ + 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1., 1.]).reshape((8, 3)) + np.testing.assert_array_almost_equal(params.deformed_control_points, + deformed_control_points_exact) + + def test_read_parameters_failing_filename_type(self): + params = rbfp.RBFParameters() + with self.assertRaises(TypeError): + params.read_parameters(3) + + def test_read_parameters_failing_number_deformed_control_points(self): + params = rbfp.RBFParameters() + with self.assertRaises(TypeError): + params.read_parameters( + 'tests/test_datasets/parameters_rbf_bugged_01.prm') + + def test_write_parameters_failing_filename_type(self): + params = rbfp.RBFParameters() + with self.assertRaises(TypeError): + params.write_parameters(5) + + def test_write_parameters_filename_default_existance(self): + params = rbfp.RBFParameters() + params.basis = 'inv_multi_quadratic_biharmonic_spline' + params.radius = 0.1 + params.n_control_points = 3 + params.original_control_points = np.array( + [0., 0., 0., 0., 0., 1., 0., 1., 0.]).reshape((3, 3)) + params.deformed_control_points = np.array( + [0., 0., 0., 0., 0., 1., 0., 1., 0.]).reshape((3, 3)) + params.write_parameters() + outfilename = 'parameters_rbf.prm' + assert os.path.isfile(outfilename) + os.remove(outfilename) + + def test_write_parameters_filename_default(self): + params = rbfp.RBFParameters() + params.basis = 'gaussian_spline' + params.radius = 0.5 + params.n_control_points = 8 + params.power = 2 + params.original_control_points = np.array([0., 0., 0., 0., 0., 1., 0., 1., 0., 1., 0., 0., \ + 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1., 1.]).reshape((8, 3)) + params.deformed_control_points = np.array([0., 0., 0., 0., 0., 1., 0., 1., 0., 1., 0., 0., \ + 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1., 1.]).reshape((8, 3)) + outfilename = 'test.prm' + params.write_parameters(outfilename) + outfilename_expected = 'tests/test_datasets/parameters_rbf_default.prm' + + print(filecmp.cmp(outfilename, outfilename_expected)) + self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) + os.remove(outfilename) + + def test_write_parameters(self): + params = rbfp.RBFParameters() + params.read_parameters('tests/test_datasets/parameters_rbf_cube.prm') + + outfilename = 'tests/test_datasets/parameters_rbf_cube_out.prm' + outfilename_expected = 'tests/test_datasets/parameters_rbf_cube_out_true.prm' + params.write_parameters(outfilename) + + self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) + os.remove(outfilename) + + def test_read_parameters_filename_default(self): + params = rbfp.RBFParameters() + params.read_parameters() + outfilename = 'parameters_rbf.prm' + outfilename_expected = 'tests/test_datasets/parameters_rbf_default.prm' + + self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) + os.remove(outfilename) + + def test_print_info(self): + params = rbfp.RBFParameters() + print(params) diff --git a/tests/test_stephandler.py b/tests/test_stephandler.py index 269c9cd..ddff0e7 100644 --- a/tests/test_stephandler.py +++ b/tests/test_stephandler.py @@ -9,227 +9,222 @@ class TestStepHandler(TestCase): - def test_step_instantiation(self): - step_handler = sh.StepHandler() - - def test_step_default_extension_member(self): - step_handler = sh.StepHandler() - self.assertListEqual(step_handler.extensions, ['.step', '.stp']) - - def test_step_parse_failing_filename_type(self): - step_handler = sh.StepHandler() - with self.assertRaises(TypeError): - mesh_points = step_handler.parse(5.2) - - def test_step_parse_failing_check_extension(self): - step_handler = sh.StepHandler() - with self.assertRaises(ValueError): - mesh_points = step_handler.parse( - 'tests/test_datasets/test_pipe.vtk' - ) - - def test_step_parse_infile(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') - self.assertEqual(step_handler.infile, 'tests/test_datasets/test_pipe.step') - - def test_step_parse_control_point_position_member(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') - self.assertListEqual( - step_handler._control_point_position, - [0, 4, 8, 14, 20, 26, 32] - ) - - def test_step_parse_shape(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') - self.assertTupleEqual(mesh_points.shape, (32, 3)) - - def test_step_parse_shape_stp(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.stp') - self.assertTupleEqual(mesh_points.shape, (32, 3)) - - def test_step_parse_coords_1(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') - np.testing.assert_almost_equal(mesh_points[6][0], 1500.0) - - def test_step_parse_coords_2(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') - np.testing.assert_almost_equal(mesh_points[8][1], -1000.0) - - def test_step_parse_coords_3(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') - np.testing.assert_almost_equal(mesh_points[30][2], 0.0) - - def test_step_parse_coords_4(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') - np.testing.assert_almost_equal(mesh_points[0][0], -1500.0) - - def test_step_parse_coords_5(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') - np.testing.assert_almost_equal(mesh_points[-1][2], 0.0) - - def test_step_parse_coords_5_stp(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.stp') - np.testing.assert_almost_equal(mesh_points[-1][2], 0.0) - - def test_step_write_failing_filename_type(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') - with self.assertRaises(TypeError): - step_handler.write(mesh_points, -2) - - def test_step_write_failing_check_extension(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') - with self.assertRaises(ValueError): - step_handler.write( - mesh_points, 'tests/test_datasets/test_square.stl' - ) - - def test_step_write_failing_infile_instantiation(self): - step_handler = sh.StepHandler() - mesh_points = np.zeros((20, 3)) - with self.assertRaises(RuntimeError): - step_handler.write( - mesh_points, 'tests/test_datasets/test_pipe_out.step' - ) - - def test_step_write_outfile(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') - outfilename = 'tests/test_datasets/test_pipe_out.step' - step_handler.write(mesh_points, outfilename) - self.assertEqual(step_handler.outfile, outfilename) - self.addCleanup(os.remove, outfilename) - - def test_step_write_outfile_tolerance(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') - outfilename = 'tests/test_datasets/test_pipe_out.step' - step_handler.write(mesh_points, outfilename, 1e-3) - self.assertEqual(step_handler.tolerance, 1e-3) - self.addCleanup(os.remove, outfilename) - - def test_step_write_modified_tolerance(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') - outfilename = 'tests/test_datasets/test_pipe_out.step' - step_handler.write(mesh_points, outfilename, 1e-3) - self.assertEqual(step_handler.outfile, outfilename) - self.addCleanup(os.remove, outfilename) - - def test_step_write_comparison_step(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') - mesh_points[0][0] = 2.2 - mesh_points[5][1] = 4.3 - mesh_points[9][2] = 0.5 - mesh_points[12][0] = 7.2 - mesh_points[16][1] = -1.2 - mesh_points[31][2] = -3.6 - - outfilename = 'tests/test_datasets/test_pipe_out.step' - outfilename_expected = 'tests/test_datasets/test_pipe_out_true.step' - - step_handler.write(mesh_points, outfilename) - - mesh_points = step_handler.parse(outfilename) - mesh_points_expected = step_handler.parse(outfilename_expected) - np.testing.assert_array_almost_equal(mesh_points, mesh_points_expected) - self.addCleanup(os.remove, outfilename) - - def test_step_write_comparison_stp(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.stp') - mesh_points[0][0] = 2.2 - mesh_points[5][1] = 4.3 - mesh_points[9][2] = 0.5 - mesh_points[12][0] = 7.2 - mesh_points[16][1] = -1.2 - mesh_points[31][2] = -3.6 - - outfilename = 'tests/test_datasets/test_pipe_out.stp' - outfilename_expected = 'tests/test_datasets/test_pipe_out_true.stp' - - step_handler.write(mesh_points, outfilename) - - mesh_points = step_handler.parse(outfilename) - mesh_points_expected = step_handler.parse(outfilename_expected) - np.testing.assert_array_almost_equal(mesh_points, mesh_points_expected) - self.addCleanup(os.remove, outfilename) - - def test_step_plot_save_fig(self): - step_handler = sh.StepHandler() - mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') - step_handler.plot(save_fig=True) - self.assertTrue(os.path.isfile('tests/test_datasets/test_pipe.png')) - self.addCleanup(os.remove, 'tests/test_datasets/test_pipe.png') - - def test_step_plot_failing_outfile_type(self): - step_handler = sh.StepHandler() - with self.assertRaises(TypeError): - step_handler.plot(plot_file=3) - - def test_step_show_failing_outfile_type(self): - step_handler = sh.StepHandler() - with self.assertRaises(TypeError): - step_handler.show(show_file=1.1) - - def test_step_load_shape_from_file_raises_wrong_type(self): - step_handler = sh.StepHandler() - with self.assertRaises(TypeError): - step_handler.load_shape_from_file(None) - - def test_step_load_shape_from_file_raises_wrong_extension(self): - step_handler = sh.StepHandler() - with self.assertRaises(ValueError): - step_handler.load_shape_from_file('tests/test_datasets/test_pipe.igs') - - def test_step_load_shape_correct_step(self): - step_handler = sh.StepHandler() - shape = step_handler.load_shape_from_file( - 'tests/test_datasets/test_pipe.step' - ) - self.assertEqual(type(shape), TopoDS_Shape) - - def test_step_load_shape_correct_stp(self): - step_handler = sh.StepHandler() - shape = step_handler.load_shape_from_file( - 'tests/test_datasets/test_pipe.stp' - ) - self.assertEqual(type(shape), TopoDS_Shape) - - def test_step_write_shape_to_file_raises_wrong_type(self): - step_handler = sh.StepHandler() - with self.assertRaises(TypeError): - step_handler.write_shape_to_file(None, None) - - def test_step_write_shape_to_file_raises_wrong_extension(self): - step_handler = sh.StepHandler() - with self.assertRaises(ValueError): - step_handler.load_shape_from_file('tests/test_datasets/x.igs') - - def test_step_write_shape_to_file_step(self): - shp = BRepPrimAPI_MakeBox(1., 1., 1.).Shape() - path = 'tests/test_datasets/x.step' - step_handler = sh.StepHandler() - step_handler.write_shape_to_file(shp, path) - self.assertTrue(os.path.exists(path)) - self.addCleanup(os.remove, path) - - def test_step_write_shape_to_file_stp(self): - shp = BRepPrimAPI_MakeBox(1., 1., 1.).Shape() - path = 'tests/test_datasets/x.stp' - step_handler = sh.StepHandler() - step_handler.write_shape_to_file(shp, path) - self.assertTrue(os.path.exists(path)) - self.addCleanup(os.remove, path) + def test_step_instantiation(self): + step_handler = sh.StepHandler() + + def test_step_default_extension_member(self): + step_handler = sh.StepHandler() + self.assertListEqual(step_handler.extensions, ['.step', '.stp']) + + def test_step_parse_failing_filename_type(self): + step_handler = sh.StepHandler() + with self.assertRaises(TypeError): + mesh_points = step_handler.parse(5.2) + + def test_step_parse_failing_check_extension(self): + step_handler = sh.StepHandler() + with self.assertRaises(ValueError): + mesh_points = step_handler.parse( + 'tests/test_datasets/test_pipe.vtk') + + def test_step_parse_infile(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') + self.assertEqual(step_handler.infile, + 'tests/test_datasets/test_pipe.step') + + def test_step_parse_control_point_position_member(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') + self.assertListEqual(step_handler._control_point_position, + [0, 4, 8, 14, 20, 26, 32]) + + def test_step_parse_shape(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') + self.assertTupleEqual(mesh_points.shape, (32, 3)) + + def test_step_parse_shape_stp(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.stp') + self.assertTupleEqual(mesh_points.shape, (32, 3)) + + def test_step_parse_coords_1(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') + np.testing.assert_almost_equal(mesh_points[6][0], 1500.0) + + def test_step_parse_coords_2(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') + np.testing.assert_almost_equal(mesh_points[8][1], -1000.0) + + def test_step_parse_coords_3(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') + np.testing.assert_almost_equal(mesh_points[30][2], 0.0) + + def test_step_parse_coords_4(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') + np.testing.assert_almost_equal(mesh_points[0][0], -1500.0) + + def test_step_parse_coords_5(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') + np.testing.assert_almost_equal(mesh_points[-1][2], 0.0) + + def test_step_parse_coords_5_stp(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.stp') + np.testing.assert_almost_equal(mesh_points[-1][2], 0.0) + + def test_step_write_failing_filename_type(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') + with self.assertRaises(TypeError): + step_handler.write(mesh_points, -2) + + def test_step_write_failing_check_extension(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') + with self.assertRaises(ValueError): + step_handler.write(mesh_points, + 'tests/test_datasets/test_square.stl') + + def test_step_write_failing_infile_instantiation(self): + step_handler = sh.StepHandler() + mesh_points = np.zeros((20, 3)) + with self.assertRaises(RuntimeError): + step_handler.write(mesh_points, + 'tests/test_datasets/test_pipe_out.step') + + def test_step_write_outfile(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') + outfilename = 'tests/test_datasets/test_pipe_out.step' + step_handler.write(mesh_points, outfilename) + self.assertEqual(step_handler.outfile, outfilename) + self.addCleanup(os.remove, outfilename) + + def test_step_write_outfile_tolerance(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') + outfilename = 'tests/test_datasets/test_pipe_out.step' + step_handler.write(mesh_points, outfilename, 1e-3) + self.assertEqual(step_handler.tolerance, 1e-3) + self.addCleanup(os.remove, outfilename) + + def test_step_write_modified_tolerance(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') + outfilename = 'tests/test_datasets/test_pipe_out.step' + step_handler.write(mesh_points, outfilename, 1e-3) + self.assertEqual(step_handler.outfile, outfilename) + self.addCleanup(os.remove, outfilename) + + def test_step_write_comparison_step(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') + mesh_points[0][0] = 2.2 + mesh_points[5][1] = 4.3 + mesh_points[9][2] = 0.5 + mesh_points[12][0] = 7.2 + mesh_points[16][1] = -1.2 + mesh_points[31][2] = -3.6 + + outfilename = 'tests/test_datasets/test_pipe_out.step' + outfilename_expected = 'tests/test_datasets/test_pipe_out_true.step' + + step_handler.write(mesh_points, outfilename) + + mesh_points = step_handler.parse(outfilename) + mesh_points_expected = step_handler.parse(outfilename_expected) + np.testing.assert_array_almost_equal(mesh_points, mesh_points_expected) + self.addCleanup(os.remove, outfilename) + + def test_step_write_comparison_stp(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.stp') + mesh_points[0][0] = 2.2 + mesh_points[5][1] = 4.3 + mesh_points[9][2] = 0.5 + mesh_points[12][0] = 7.2 + mesh_points[16][1] = -1.2 + mesh_points[31][2] = -3.6 + + outfilename = 'tests/test_datasets/test_pipe_out.stp' + outfilename_expected = 'tests/test_datasets/test_pipe_out_true.stp' + + step_handler.write(mesh_points, outfilename) + + mesh_points = step_handler.parse(outfilename) + mesh_points_expected = step_handler.parse(outfilename_expected) + np.testing.assert_array_almost_equal(mesh_points, mesh_points_expected) + self.addCleanup(os.remove, outfilename) + + def test_step_plot_save_fig(self): + step_handler = sh.StepHandler() + mesh_points = step_handler.parse('tests/test_datasets/test_pipe.step') + step_handler.plot(save_fig=True) + self.assertTrue(os.path.isfile('tests/test_datasets/test_pipe.png')) + self.addCleanup(os.remove, 'tests/test_datasets/test_pipe.png') + + def test_step_plot_failing_outfile_type(self): + step_handler = sh.StepHandler() + with self.assertRaises(TypeError): + step_handler.plot(plot_file=3) + + def test_step_show_failing_outfile_type(self): + step_handler = sh.StepHandler() + with self.assertRaises(TypeError): + step_handler.show(show_file=1.1) + + def test_step_load_shape_from_file_raises_wrong_type(self): + step_handler = sh.StepHandler() + with self.assertRaises(TypeError): + step_handler.load_shape_from_file(None) + + def test_step_load_shape_from_file_raises_wrong_extension(self): + step_handler = sh.StepHandler() + with self.assertRaises(ValueError): + step_handler.load_shape_from_file( + 'tests/test_datasets/test_pipe.igs') + + def test_step_load_shape_correct_step(self): + step_handler = sh.StepHandler() + shape = step_handler.load_shape_from_file( + 'tests/test_datasets/test_pipe.step') + self.assertEqual(type(shape), TopoDS_Shape) + + def test_step_load_shape_correct_stp(self): + step_handler = sh.StepHandler() + shape = step_handler.load_shape_from_file( + 'tests/test_datasets/test_pipe.stp') + self.assertEqual(type(shape), TopoDS_Shape) + + def test_step_write_shape_to_file_raises_wrong_type(self): + step_handler = sh.StepHandler() + with self.assertRaises(TypeError): + step_handler.write_shape_to_file(None, None) + + def test_step_write_shape_to_file_raises_wrong_extension(self): + step_handler = sh.StepHandler() + with self.assertRaises(ValueError): + step_handler.load_shape_from_file('tests/test_datasets/x.igs') + + def test_step_write_shape_to_file_step(self): + shp = BRepPrimAPI_MakeBox(1., 1., 1.).Shape() + path = 'tests/test_datasets/x.step' + step_handler = sh.StepHandler() + step_handler.write_shape_to_file(shp, path) + self.assertTrue(os.path.exists(path)) + self.addCleanup(os.remove, path) + + def test_step_write_shape_to_file_stp(self): + shp = BRepPrimAPI_MakeBox(1., 1., 1.).Shape() + path = 'tests/test_datasets/x.stp' + step_handler = sh.StepHandler() + step_handler.write_shape_to_file(shp, path) + self.assertTrue(os.path.exists(path)) + self.addCleanup(os.remove, path) diff --git a/tests/test_stlhandler.py b/tests/test_stlhandler.py index 878c66c..47f31f1 100644 --- a/tests/test_stlhandler.py +++ b/tests/test_stlhandler.py @@ -7,217 +7,209 @@ class TestStlHandler(TestCase): - def test_stl_instantiation(self): - stl_handler = sh.StlHandler() - - def test_stl_default_infile_member(self): - stl_handler = sh.StlHandler() - self.assertIsNone(stl_handler.infile) - - def test_stl_default_outfile_member(self): - stl_handler = sh.StlHandler() - self.assertIsNone(stl_handler.outfile) - - def test_stl_default_extension_member(self): - stl_handler = sh.StlHandler() - self.assertListEqual(stl_handler.extensions, ['.stl']) - - def test_stl_parse_failing_filename_type(self): - stl_handler = sh.StlHandler() - with self.assertRaises(TypeError): - mesh_points = stl_handler.parse(5.2) - - def test_stl_parse_failing_check_extension(self): - stl_handler = sh.StlHandler() - with self.assertRaises(ValueError): - mesh_points = stl_handler.parse( - 'tests/test_datasets/test_square.iges' - ) - - def test_stl_parse_infile(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') - self.assertEqual(stl_handler.infile, 'tests/test_datasets/test_sphere.stl') - - def test_stl_parse_shape(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') - self.assertTupleEqual(mesh_points.shape, (1202, 3)) - - def test_stl_parse_coords_1(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') - np.testing.assert_almost_equal(mesh_points[33][0], -17.51774978) - - def test_stl_parse_coords_2(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') - np.testing.assert_almost_equal(mesh_points[998][1], 11.18433952) - - def test_stl_parse_coords_3(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') - np.testing.assert_almost_equal(mesh_points[557][2], 2.47205805) - - def test_stl_parse_coords_4(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') - np.testing.assert_almost_equal(mesh_points[0][0], -21.31975937) - - def test_stl_parse_coords_5(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') - np.testing.assert_almost_equal(mesh_points[-2][2], -39.05963898) - - def test_stl_parse_coords_5_bin(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse( - 'tests/test_datasets/test_sphere_bin.stl' - ) - np.testing.assert_almost_equal(mesh_points[-2][2], -39.05963898) - - def test_stl_write_failing_filename_type(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') - with self.assertRaises(TypeError): - stl_handler.write(mesh_points, 4.) - - def test_stl_write_failing_check_extension(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') - with self.assertRaises(ValueError): - stl_handler.write( - mesh_points, 'tests/test_datasets/test_square.iges' - ) - - def test_stl_write_failing_infile_instantiation(self): - stl_handler = sh.StlHandler() - mesh_points = np.zeros((40, 3)) - with self.assertRaises(RuntimeError): - stl_handler.write( - mesh_points, 'tests/test_datasets/test_sphere_out.stl' - ) - - def test_stl_write_outfile(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') - outfilename = 'tests/test_datasets/test_sphere_out.stl' - stl_handler.write(mesh_points, outfilename) - self.assertEqual(stl_handler.outfile, outfilename) - self.addCleanup(os.remove, outfilename) - - def test_stl_write_comparison(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') - mesh_points[0] = [-40.2, -20.5, 60.9] - mesh_points[1] = [-40.2, -10.5, 60.9] - mesh_points[2] = [-40.2, -10.5, 60.9] - mesh_points[500] = [-40.2, -20.5, 60.9] - mesh_points[501] = [-40.2, -10.5, 60.9] - mesh_points[502] = [-40.2, -10.5, 60.9] - mesh_points[1000] = [-40.2, -20.5, 60.9] - mesh_points[1001] = [-40.2, -10.5, 60.9] - mesh_points[1002] = [-40.2, -10.5, 60.9] - - outfilename = 'tests/test_datasets/test_sphere_out.stl' - outfilename_expected = 'tests/test_datasets/test_sphere_out_true.stl' - - stl_handler.write(mesh_points, outfilename) - self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) - self.addCleanup(os.remove, outfilename) - - def test_stl_write_binary_from_binary(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse( - 'tests/test_datasets/test_sphere_bin.stl' - ) - mesh_points[0] = [-40.2, -20.5, 60.9] - mesh_points[1] = [-40.2, -10.5, 60.9] - mesh_points[2] = [-40.2, -10.5, 60.9] - mesh_points[500] = [-40.2, -20.5, 60.9] - mesh_points[501] = [-40.2, -10.5, 60.9] - mesh_points[502] = [-40.2, -10.5, 60.9] - mesh_points[1000] = [-40.2, -20.5, 60.9] - mesh_points[1001] = [-40.2, -10.5, 60.9] - mesh_points[1002] = [-40.2, -10.5, 60.9] - - outfilename = 'tests/test_datasets/test_sphere_out.stl' - - stl_handler.write(mesh_points, outfilename, write_bin=True) - self.addCleanup(os.remove, outfilename) - - def test_stl_write_binary_from_ascii(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') - mesh_points[0] = [-40.2, -20.5, 60.9] - mesh_points[1] = [-40.2, -10.5, 60.9] - mesh_points[2] = [-40.2, -10.5, 60.9] - mesh_points[500] = [-40.2, -20.5, 60.9] - mesh_points[501] = [-40.2, -10.5, 60.9] - mesh_points[502] = [-40.2, -10.5, 60.9] - mesh_points[1000] = [-40.2, -20.5, 60.9] - mesh_points[1001] = [-40.2, -10.5, 60.9] - mesh_points[1002] = [-40.2, -10.5, 60.9] - - outfilename = 'tests/test_datasets/test_sphere_out.stl' - - stl_handler.write(mesh_points, outfilename, write_bin=True) - self.addCleanup(os.remove, outfilename) - - def test_stl_write_ascii_from_binary(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse( - 'tests/test_datasets/test_sphere_bin.stl' - ) - mesh_points[0] = [-40.2, -20.5, 60.9] - mesh_points[1] = [-40.2, -10.5, 60.9] - mesh_points[2] = [-40.2, -10.5, 60.9] - mesh_points[500] = [-40.2, -20.5, 60.9] - mesh_points[501] = [-40.2, -10.5, 60.9] - mesh_points[502] = [-40.2, -10.5, 60.9] - mesh_points[1000] = [-40.2, -20.5, 60.9] - mesh_points[1001] = [-40.2, -10.5, 60.9] - mesh_points[1002] = [-40.2, -10.5, 60.9] - - outfilename = 'tests/test_datasets/test_sphere_out.stl' - outfilename_expected = 'tests/test_datasets/test_sphere_out_true.stl' - - stl_handler.write(mesh_points, outfilename, write_bin=False) - self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) - self.addCleanup(os.remove, outfilename) - - def test_stl_plot_save_fig(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') - stl_handler.plot(save_fig=True) - self.assertTrue(os.path.isfile('tests/test_datasets/test_sphere.png')) - self.addCleanup(os.remove, 'tests/test_datasets/test_sphere.png') - - def test_stl_plot_save_fig_bin(self): - stl_handler = sh.StlHandler() - mesh_points = stl_handler.parse( - 'tests/test_datasets/test_sphere_bin.stl' - ) - stl_handler.plot(save_fig=True) - self.assertTrue( - os.path.isfile('tests/test_datasets/test_sphere_bin.png') - ) - self.addCleanup(os.remove, 'tests/test_datasets/test_sphere_bin.png') - - def test_stl_plot_save_fig_plot_file(self): - stl_handler = sh.StlHandler() - stl_handler.plot( - plot_file='tests/test_datasets/test_sphere.stl', save_fig=True - ) - self.assertTrue(os.path.isfile('tests/test_datasets/test_sphere.png')) - self.addCleanup(os.remove, 'tests/test_datasets/test_sphere.png') - - def test_stl_plot_failing_outfile_type(self): - stl_handler = sh.StlHandler() - with self.assertRaises(TypeError): - stl_handler.plot(plot_file=3) - - def test_stl_show_fail(self): - stl_handler = sh.StlHandler() - with self.assertRaises(TypeError): - stl_handler.show(show_file=3) + def test_stl_instantiation(self): + stl_handler = sh.StlHandler() + + def test_stl_default_infile_member(self): + stl_handler = sh.StlHandler() + self.assertIsNone(stl_handler.infile) + + def test_stl_default_outfile_member(self): + stl_handler = sh.StlHandler() + self.assertIsNone(stl_handler.outfile) + + def test_stl_default_extension_member(self): + stl_handler = sh.StlHandler() + self.assertListEqual(stl_handler.extensions, ['.stl']) + + def test_stl_parse_failing_filename_type(self): + stl_handler = sh.StlHandler() + with self.assertRaises(TypeError): + mesh_points = stl_handler.parse(5.2) + + def test_stl_parse_failing_check_extension(self): + stl_handler = sh.StlHandler() + with self.assertRaises(ValueError): + mesh_points = stl_handler.parse( + 'tests/test_datasets/test_square.iges') + + def test_stl_parse_infile(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') + self.assertEqual(stl_handler.infile, + 'tests/test_datasets/test_sphere.stl') + + def test_stl_parse_shape(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') + self.assertTupleEqual(mesh_points.shape, (1202, 3)) + + def test_stl_parse_coords_1(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') + np.testing.assert_almost_equal(mesh_points[33][0], -17.51774978) + + def test_stl_parse_coords_2(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') + np.testing.assert_almost_equal(mesh_points[998][1], 11.18433952) + + def test_stl_parse_coords_3(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') + np.testing.assert_almost_equal(mesh_points[557][2], 2.47205805) + + def test_stl_parse_coords_4(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') + np.testing.assert_almost_equal(mesh_points[0][0], -21.31975937) + + def test_stl_parse_coords_5(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') + np.testing.assert_almost_equal(mesh_points[-2][2], -39.05963898) + + def test_stl_parse_coords_5_bin(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse( + 'tests/test_datasets/test_sphere_bin.stl') + np.testing.assert_almost_equal(mesh_points[-2][2], -39.05963898) + + def test_stl_write_failing_filename_type(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') + with self.assertRaises(TypeError): + stl_handler.write(mesh_points, 4.) + + def test_stl_write_failing_check_extension(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') + with self.assertRaises(ValueError): + stl_handler.write(mesh_points, + 'tests/test_datasets/test_square.iges') + + def test_stl_write_failing_infile_instantiation(self): + stl_handler = sh.StlHandler() + mesh_points = np.zeros((40, 3)) + with self.assertRaises(RuntimeError): + stl_handler.write(mesh_points, + 'tests/test_datasets/test_sphere_out.stl') + + def test_stl_write_outfile(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') + outfilename = 'tests/test_datasets/test_sphere_out.stl' + stl_handler.write(mesh_points, outfilename) + self.assertEqual(stl_handler.outfile, outfilename) + self.addCleanup(os.remove, outfilename) + + def test_stl_write_comparison(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') + mesh_points[0] = [-40.2, -20.5, 60.9] + mesh_points[1] = [-40.2, -10.5, 60.9] + mesh_points[2] = [-40.2, -10.5, 60.9] + mesh_points[500] = [-40.2, -20.5, 60.9] + mesh_points[501] = [-40.2, -10.5, 60.9] + mesh_points[502] = [-40.2, -10.5, 60.9] + mesh_points[1000] = [-40.2, -20.5, 60.9] + mesh_points[1001] = [-40.2, -10.5, 60.9] + mesh_points[1002] = [-40.2, -10.5, 60.9] + + outfilename = 'tests/test_datasets/test_sphere_out.stl' + outfilename_expected = 'tests/test_datasets/test_sphere_out_true.stl' + + stl_handler.write(mesh_points, outfilename) + self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) + self.addCleanup(os.remove, outfilename) + + def test_stl_write_binary_from_binary(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse( + 'tests/test_datasets/test_sphere_bin.stl') + mesh_points[0] = [-40.2, -20.5, 60.9] + mesh_points[1] = [-40.2, -10.5, 60.9] + mesh_points[2] = [-40.2, -10.5, 60.9] + mesh_points[500] = [-40.2, -20.5, 60.9] + mesh_points[501] = [-40.2, -10.5, 60.9] + mesh_points[502] = [-40.2, -10.5, 60.9] + mesh_points[1000] = [-40.2, -20.5, 60.9] + mesh_points[1001] = [-40.2, -10.5, 60.9] + mesh_points[1002] = [-40.2, -10.5, 60.9] + + outfilename = 'tests/test_datasets/test_sphere_out.stl' + + stl_handler.write(mesh_points, outfilename, write_bin=True) + self.addCleanup(os.remove, outfilename) + + def test_stl_write_binary_from_ascii(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') + mesh_points[0] = [-40.2, -20.5, 60.9] + mesh_points[1] = [-40.2, -10.5, 60.9] + mesh_points[2] = [-40.2, -10.5, 60.9] + mesh_points[500] = [-40.2, -20.5, 60.9] + mesh_points[501] = [-40.2, -10.5, 60.9] + mesh_points[502] = [-40.2, -10.5, 60.9] + mesh_points[1000] = [-40.2, -20.5, 60.9] + mesh_points[1001] = [-40.2, -10.5, 60.9] + mesh_points[1002] = [-40.2, -10.5, 60.9] + + outfilename = 'tests/test_datasets/test_sphere_out.stl' + + stl_handler.write(mesh_points, outfilename, write_bin=True) + self.addCleanup(os.remove, outfilename) + + def test_stl_write_ascii_from_binary(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse( + 'tests/test_datasets/test_sphere_bin.stl') + mesh_points[0] = [-40.2, -20.5, 60.9] + mesh_points[1] = [-40.2, -10.5, 60.9] + mesh_points[2] = [-40.2, -10.5, 60.9] + mesh_points[500] = [-40.2, -20.5, 60.9] + mesh_points[501] = [-40.2, -10.5, 60.9] + mesh_points[502] = [-40.2, -10.5, 60.9] + mesh_points[1000] = [-40.2, -20.5, 60.9] + mesh_points[1001] = [-40.2, -10.5, 60.9] + mesh_points[1002] = [-40.2, -10.5, 60.9] + + outfilename = 'tests/test_datasets/test_sphere_out.stl' + outfilename_expected = 'tests/test_datasets/test_sphere_out_true.stl' + + stl_handler.write(mesh_points, outfilename, write_bin=False) + self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) + self.addCleanup(os.remove, outfilename) + + def test_stl_plot_save_fig(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse('tests/test_datasets/test_sphere.stl') + stl_handler.plot(save_fig=True) + self.assertTrue(os.path.isfile('tests/test_datasets/test_sphere.png')) + self.addCleanup(os.remove, 'tests/test_datasets/test_sphere.png') + + def test_stl_plot_save_fig_bin(self): + stl_handler = sh.StlHandler() + mesh_points = stl_handler.parse( + 'tests/test_datasets/test_sphere_bin.stl') + stl_handler.plot(save_fig=True) + self.assertTrue( + os.path.isfile('tests/test_datasets/test_sphere_bin.png')) + self.addCleanup(os.remove, 'tests/test_datasets/test_sphere_bin.png') + + def test_stl_plot_save_fig_plot_file(self): + stl_handler = sh.StlHandler() + stl_handler.plot( + plot_file='tests/test_datasets/test_sphere.stl', save_fig=True) + self.assertTrue(os.path.isfile('tests/test_datasets/test_sphere.png')) + self.addCleanup(os.remove, 'tests/test_datasets/test_sphere.png') + + def test_stl_plot_failing_outfile_type(self): + stl_handler = sh.StlHandler() + with self.assertRaises(TypeError): + stl_handler.plot(plot_file=3) + + def test_stl_show_fail(self): + stl_handler = sh.StlHandler() + with self.assertRaises(TypeError): + stl_handler.show(show_file=3) diff --git a/tests/test_unvhandler.py b/tests/test_unvhandler.py index 47b7cd0..34b6b26 100644 --- a/tests/test_unvhandler.py +++ b/tests/test_unvhandler.py @@ -7,123 +7,121 @@ class TestUnvHandler(TestCase): - def test_unv_instantiation(self): - unv_handler = uh.UnvHandler() - - def test_unv_default_infile_member(self): - unv_handler = uh.UnvHandler() - self.assertIsNone(unv_handler.infile) - - def test_unv_default_outfile_member(self): - unv_handler = uh.UnvHandler() - self.assertIsNone(unv_handler.outfile) - - def test_unv_default_extension_member(self): - unv_handler = uh.UnvHandler() - self.assertListEqual(unv_handler.extensions, ['.unv']) - - def test_unv_parse_failing_filename_type(self): - unv_handler = uh.UnvHandler() - with self.assertRaises(TypeError): - mesh_points = unv_handler.parse(5.2) - - def test_unv_parse_failing_check_extension(self): - unv_handler = uh.UnvHandler() - with self.assertRaises(ValueError): - mesh_points = unv_handler.parse( - 'tests/test_datasets/test_square.iges' - ) - - def test_unv_parse_infile(self): - unv_handler = uh.UnvHandler() - mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') - self.assertEqual(unv_handler.infile, 'tests/test_datasets/test_square.unv') - - def test_unv_parse_shape(self): - unv_handler = uh.UnvHandler() - mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') - self.assertTupleEqual(mesh_points.shape, (256, 3)) - - def test_unv_parse_coords_1(self): - unv_handler = uh.UnvHandler() - mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') - np.testing.assert_almost_equal(mesh_points[33][0], 1.0) - - def test_unv_parse_coords_2(self): - unv_handler = uh.UnvHandler() - mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') - np.testing.assert_almost_equal(mesh_points[178][1], 0.4) - - def test_unv_parse_coords_3(self): - unv_handler = uh.UnvHandler() - mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') - np.testing.assert_almost_equal(mesh_points[100][2], 0.0) - - def test_unv_parse_coords_4(self): - unv_handler = uh.UnvHandler() - mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') - np.testing.assert_almost_equal(mesh_points[0][0], 0.0) - - def test_unv_parse_coords_5(self): - unv_handler = uh.UnvHandler() - mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') - np.testing.assert_almost_equal(mesh_points[-1][2], 0.0) - - def test_unv_write_failing_filename_type(self): - unv_handler = uh.UnvHandler() - mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') - with self.assertRaises(TypeError): - unv_handler.write(mesh_points, -2) - - def test_unv_write_failing_check_extension(self): - unv_handler = uh.UnvHandler() - mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') - with self.assertRaises(ValueError): - unv_handler.write( - mesh_points, 'tests/test_datasets/test_square.iges' - ) - - def test_unv_write_failing_infile_instantiation(self): - unv_handler = uh.UnvHandler() - mesh_points = np.zeros((20, 3)) - with self.assertRaises(RuntimeError): - unv_handler.write( - mesh_points, 'tests/test_datasets/test_square_out.unv' - ) - - def test_unv_write_outfile(self): - unv_handler = uh.UnvHandler() - mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') - outfilename = 'tests/test_datasets/test_square_out.unv' - unv_handler.write(mesh_points, outfilename) - self.assertEqual(unv_handler.outfile, outfilename) - self.addCleanup(os.remove, outfilename) - - def test_unv_write_comparison_1(self): - unv_handler = uh.UnvHandler() - mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') - - outfilename = 'tests/test_datasets/test_square_out.unv' - outfilename_expected = 'tests/test_datasets/test_square.unv' - - unv_handler.write(mesh_points, outfilename) - self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) - self.addCleanup(os.remove, outfilename) - - def test_unv_write_comparison_2(self): - unv_handler = uh.UnvHandler() - mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') - - mesh_points[0][0] = 2.2 - mesh_points[5][1] = 4.3 - mesh_points[9][2] = 0.5 - mesh_points[45][0] = 7.2 - mesh_points[132][1] = -1.2 - mesh_points[255][2] = -3.6 - - outfilename = 'tests/test_datasets/test_square_out.unv' - outfilename_expected = 'tests/test_datasets/test_square_out_true.unv' - - unv_handler.write(mesh_points, outfilename) - self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) - self.addCleanup(os.remove, outfilename) + def test_unv_instantiation(self): + unv_handler = uh.UnvHandler() + + def test_unv_default_infile_member(self): + unv_handler = uh.UnvHandler() + self.assertIsNone(unv_handler.infile) + + def test_unv_default_outfile_member(self): + unv_handler = uh.UnvHandler() + self.assertIsNone(unv_handler.outfile) + + def test_unv_default_extension_member(self): + unv_handler = uh.UnvHandler() + self.assertListEqual(unv_handler.extensions, ['.unv']) + + def test_unv_parse_failing_filename_type(self): + unv_handler = uh.UnvHandler() + with self.assertRaises(TypeError): + mesh_points = unv_handler.parse(5.2) + + def test_unv_parse_failing_check_extension(self): + unv_handler = uh.UnvHandler() + with self.assertRaises(ValueError): + mesh_points = unv_handler.parse( + 'tests/test_datasets/test_square.iges') + + def test_unv_parse_infile(self): + unv_handler = uh.UnvHandler() + mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') + self.assertEqual(unv_handler.infile, + 'tests/test_datasets/test_square.unv') + + def test_unv_parse_shape(self): + unv_handler = uh.UnvHandler() + mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') + self.assertTupleEqual(mesh_points.shape, (256, 3)) + + def test_unv_parse_coords_1(self): + unv_handler = uh.UnvHandler() + mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') + np.testing.assert_almost_equal(mesh_points[33][0], 1.0) + + def test_unv_parse_coords_2(self): + unv_handler = uh.UnvHandler() + mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') + np.testing.assert_almost_equal(mesh_points[178][1], 0.4) + + def test_unv_parse_coords_3(self): + unv_handler = uh.UnvHandler() + mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') + np.testing.assert_almost_equal(mesh_points[100][2], 0.0) + + def test_unv_parse_coords_4(self): + unv_handler = uh.UnvHandler() + mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') + np.testing.assert_almost_equal(mesh_points[0][0], 0.0) + + def test_unv_parse_coords_5(self): + unv_handler = uh.UnvHandler() + mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') + np.testing.assert_almost_equal(mesh_points[-1][2], 0.0) + + def test_unv_write_failing_filename_type(self): + unv_handler = uh.UnvHandler() + mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') + with self.assertRaises(TypeError): + unv_handler.write(mesh_points, -2) + + def test_unv_write_failing_check_extension(self): + unv_handler = uh.UnvHandler() + mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') + with self.assertRaises(ValueError): + unv_handler.write(mesh_points, + 'tests/test_datasets/test_square.iges') + + def test_unv_write_failing_infile_instantiation(self): + unv_handler = uh.UnvHandler() + mesh_points = np.zeros((20, 3)) + with self.assertRaises(RuntimeError): + unv_handler.write(mesh_points, + 'tests/test_datasets/test_square_out.unv') + + def test_unv_write_outfile(self): + unv_handler = uh.UnvHandler() + mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') + outfilename = 'tests/test_datasets/test_square_out.unv' + unv_handler.write(mesh_points, outfilename) + self.assertEqual(unv_handler.outfile, outfilename) + self.addCleanup(os.remove, outfilename) + + def test_unv_write_comparison_1(self): + unv_handler = uh.UnvHandler() + mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') + + outfilename = 'tests/test_datasets/test_square_out.unv' + outfilename_expected = 'tests/test_datasets/test_square.unv' + + unv_handler.write(mesh_points, outfilename) + self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) + self.addCleanup(os.remove, outfilename) + + def test_unv_write_comparison_2(self): + unv_handler = uh.UnvHandler() + mesh_points = unv_handler.parse('tests/test_datasets/test_square.unv') + + mesh_points[0][0] = 2.2 + mesh_points[5][1] = 4.3 + mesh_points[9][2] = 0.5 + mesh_points[45][0] = 7.2 + mesh_points[132][1] = -1.2 + mesh_points[255][2] = -3.6 + + outfilename = 'tests/test_datasets/test_square_out.unv' + outfilename_expected = 'tests/test_datasets/test_square_out_true.unv' + + unv_handler.write(mesh_points, outfilename) + self.assertTrue(filecmp.cmp(outfilename, outfilename_expected)) + self.addCleanup(os.remove, outfilename) diff --git a/tests/test_utils.py b/tests/test_utils.py index b4e331b..2b75433 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -9,93 +9,87 @@ class TestUtils(TestCase): - def cmp(self, f1, f2): - """ - Check if the two files have the same content, skipping comment lines - """ - content1 = [line for line in open(f1) if not line.startswith('#')] - content2 = [line for line in open(f1) if not line.startswith('#')] - return content1 == content2 - - def test_utils_write_original_box(self): - params = pars.FFDParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_test_ffd_sphere.prm' - ) - - outfilename = 'tests/test_datasets/box_test_sphere.vtk' - - ut.write_bounding_box(params, outfilename, write_deformed=False) - os.remove(outfilename) - - def test_utils_write_modified_box(self): - params = pars.FFDParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_test_ffd_sphere.prm' - ) - - outfilename = 'tests/test_datasets/box_test_sphere.vtk' - - ut.write_bounding_box(params, outfilename) - os.remove(outfilename) - - def test_utils_check_vtk_original_box(self): - params = pars.FFDParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_test_ffd_sphere.prm' - ) - - outfilename = 'tests/test_datasets/box_test_sphere.vtk' - if vtk.VTK_MAJOR_VERSION <= 5: - outfilename_expected = 'tests/test_datasets/box_test_sphere_true_version5.vtk' - else: - outfilename_expected = 'tests/test_datasets/box_test_sphere_true_version6.vtk' - - ut.write_bounding_box(params, outfilename, write_deformed=False) - - self.assertTrue(self.cmp(outfilename, outfilename_expected)) - os.remove(outfilename) - - def test_utils_check_vtk_modified_box(self): - params = pars.FFDParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_test_ffd_sphere.prm' - ) - - outfilename = 'tests/test_datasets/box_test_sphere.vtk' - if vtk.VTK_MAJOR_VERSION <= 5: - outfilename_expected = 'tests/test_datasets/box_modified_test_sphere_true_version5.vtk' - else: - outfilename_expected = 'tests/test_datasets/box_modified_test_sphere_true_version6.vtk' - - ut.write_bounding_box(params, outfilename) - - self.assertTrue(self.cmp(outfilename, outfilename_expected)) - os.remove(outfilename) - - def test_utils_plot_rbf_control_points(self): - params = pars.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_cube.prm' - ) - ut.plot_rbf_control_points(params, save_fig=False) - - def test_utils_plot_rbf_control_points_save_fig(self): - params = pars.RBFParameters() - params.read_parameters( - filename='tests/test_datasets/parameters_rbf_cube.prm' - ) - ut.plot_rbf_control_points(params, save_fig=True) - self.assertTrue(os.path.isfile('RBF_control_points.png')) - os.remove('RBF_control_points.png') - - def test_utils_check_write_points_in_vtp(self): - ctrl_points = np.arange(12).reshape(4, 3) - - outfilename = 'tests/test_datasets/points_test.vtp' - if vtk.VTK_MAJOR_VERSION >= 6: - outfilename_expected = 'tests/test_datasets/points_test_true_version6.vtp' - - ut.write_points_in_vtp(ctrl_points, outfile=outfilename) - self.assertTrue(self.cmp(outfilename, outfilename_expected)) - os.remove(outfilename) \ No newline at end of file + def cmp(self, f1, f2): + """ + Check if the two files have the same content, skipping comment lines + """ + content1 = [line for line in open(f1) if not line.startswith('#')] + content2 = [line for line in open(f1) if not line.startswith('#')] + return content1 == content2 + + def test_utils_write_original_box(self): + params = pars.FFDParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_test_ffd_sphere.prm') + + outfilename = 'tests/test_datasets/box_test_sphere.vtk' + + ut.write_bounding_box(params, outfilename, write_deformed=False) + os.remove(outfilename) + + def test_utils_write_modified_box(self): + params = pars.FFDParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_test_ffd_sphere.prm') + + outfilename = 'tests/test_datasets/box_test_sphere.vtk' + + ut.write_bounding_box(params, outfilename) + os.remove(outfilename) + + def test_utils_check_vtk_original_box(self): + params = pars.FFDParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_test_ffd_sphere.prm') + + outfilename = 'tests/test_datasets/box_test_sphere.vtk' + if vtk.VTK_MAJOR_VERSION <= 5: + outfilename_expected = 'tests/test_datasets/box_test_sphere_true_version5.vtk' + else: + outfilename_expected = 'tests/test_datasets/box_test_sphere_true_version6.vtk' + + ut.write_bounding_box(params, outfilename, write_deformed=False) + + self.assertTrue(self.cmp(outfilename, outfilename_expected)) + os.remove(outfilename) + + def test_utils_check_vtk_modified_box(self): + params = pars.FFDParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_test_ffd_sphere.prm') + + outfilename = 'tests/test_datasets/box_test_sphere.vtk' + if vtk.VTK_MAJOR_VERSION <= 5: + outfilename_expected = 'tests/test_datasets/box_modified_test_sphere_true_version5.vtk' + else: + outfilename_expected = 'tests/test_datasets/box_modified_test_sphere_true_version6.vtk' + + ut.write_bounding_box(params, outfilename) + + self.assertTrue(self.cmp(outfilename, outfilename_expected)) + os.remove(outfilename) + + def test_utils_plot_rbf_control_points(self): + params = pars.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_cube.prm') + ut.plot_rbf_control_points(params, save_fig=False) + + def test_utils_plot_rbf_control_points_save_fig(self): + params = pars.RBFParameters() + params.read_parameters( + filename='tests/test_datasets/parameters_rbf_cube.prm') + ut.plot_rbf_control_points(params, save_fig=True) + self.assertTrue(os.path.isfile('RBF_control_points.png')) + os.remove('RBF_control_points.png') + + def test_utils_check_write_points_in_vtp(self): + ctrl_points = np.arange(12).reshape(4, 3) + + outfilename = 'tests/test_datasets/points_test.vtp' + if vtk.VTK_MAJOR_VERSION >= 6: + outfilename_expected = 'tests/test_datasets/points_test_true_version6.vtp' + + ut.write_points_in_vtp(ctrl_points, outfile=outfilename) + self.assertTrue(self.cmp(outfilename, outfilename_expected)) + os.remove(outfilename) diff --git a/tests/test_vtkhandler.py b/tests/test_vtkhandler.py index ae59be1..5124515 100644 --- a/tests/test_vtkhandler.py +++ b/tests/test_vtkhandler.py @@ -7,177 +7,159 @@ class TestVtkHandler(TestCase): - def cmp(self, f1, f2): - """ - Check if the two files have the same content, skipping comment lines - """ - content1 = [line for line in open(f1) if not line.startswith('#')] - content2 = [line for line in open(f1) if not line.startswith('#')] - return content1 == content2 - - def test_vtk_instantiation(self): - vtk_handler = vh.VtkHandler() - - def test_vtk_default_infile_member(self): - vtk_handler = vh.VtkHandler() - assert vtk_handler.infile == None - - def test_vtk_default_outfile_member(self): - vtk_handler = vh.VtkHandler() - assert vtk_handler.outfile == None - - def test_vtk_default_extension_member(self): - vtk_handler = vh.VtkHandler() - self.assertListEqual(vtk_handler.extensions, ['.vtk']) - - def test_vtk_parse_failing_filename_type(self): - vtk_handler = vh.VtkHandler() - with self.assertRaises(TypeError): - mesh_points = vtk_handler.parse(5.2) - - def test_vtk_parse_failing_check_extension(self): - vtk_handler = vh.VtkHandler() - with self.assertRaises(ValueError): - mesh_points = vtk_handler.parse( - 'tests/test_datasets/test_square.iges' - ) - - def test_vtk_parse_infile(self): - vtk_handler = vh.VtkHandler() - mesh_points = vtk_handler.parse( - 'tests/test_datasets/test_red_blood_cell.vtk' - ) - assert vtk_handler.infile == 'tests/test_datasets/test_red_blood_cell.vtk' - - def test_vtk_parse_shape(self): - vtk_handler = vh.VtkHandler() - mesh_points = vtk_handler.parse( - 'tests/test_datasets/test_red_blood_cell.vtk' - ) - assert mesh_points.shape == (500, 3) - - def test_vtk_parse_coords_1(self): - vtk_handler = vh.VtkHandler() - mesh_points = vtk_handler.parse( - 'tests/test_datasets/test_red_blood_cell.vtk' - ) - np.testing.assert_almost_equal(mesh_points[33][0], -2.2977099) - - def test_vtk_parse_coords_2(self): - vtk_handler = vh.VtkHandler() - mesh_points = vtk_handler.parse( - 'tests/test_datasets/test_red_blood_cell.vtk' - ) - np.testing.assert_almost_equal(mesh_points[178][1], 0.143506) - - def test_vtk_parse_coords_3(self): - vtk_handler = vh.VtkHandler() - mesh_points = vtk_handler.parse( - 'tests/test_datasets/test_red_blood_cell.vtk' - ) - np.testing.assert_almost_equal(mesh_points[100][2], 2.3306999) - - def test_vtk_parse_coords_4(self): - vtk_handler = vh.VtkHandler() - mesh_points = vtk_handler.parse( - 'tests/test_datasets/test_red_blood_cell.vtk' - ) - np.testing.assert_almost_equal(mesh_points[0][0], -3.42499995) - - def test_vtk_parse_coords_5(self): - vtk_handler = vh.VtkHandler() - mesh_points = vtk_handler.parse( - 'tests/test_datasets/test_red_blood_cell.vtk' - ) - np.testing.assert_almost_equal(mesh_points[-1][2], -2.8480699) - - def test_vtk_write_failing_filename_type(self): - vtk_handler = vh.VtkHandler() - mesh_points = vtk_handler.parse( - 'tests/test_datasets/test_red_blood_cell.vtk' - ) - with self.assertRaises(TypeError): - vtk_handler.write(mesh_points, -2) - - def test_vtk_write_failing_check_extension(self): - vtk_handler = vh.VtkHandler() - mesh_points = vtk_handler.parse( - 'tests/test_datasets/test_red_blood_cell.vtk' - ) - with self.assertRaises(ValueError): - vtk_handler.write( - mesh_points, 'tests/test_datasets/test_square.iges' - ) - - def test_vtk_write_failing_infile_instantiation(self): - vtk_handler = vh.VtkHandler() - mesh_points = np.zeros((20, 3)) - with self.assertRaises(RuntimeError): - vtk_handler.write( - mesh_points, 'tests/test_datasets/test_red_blood_cell_out.vtk' - ) - - def test_vtk_write_outfile(self): - vtk_handler = vh.VtkHandler() - mesh_points = vtk_handler.parse( - 'tests/test_datasets/test_red_blood_cell.vtk' - ) - outfilename = 'tests/test_datasets/test_red_blood_cell_out.vtk' - vtk_handler.write(mesh_points, outfilename) - assert vtk_handler.outfile == outfilename - os.remove(outfilename) - - def test_vtk_write_comparison(self): - import vtk - vtk_handler = vh.VtkHandler() - mesh_points = vtk_handler.parse( - 'tests/test_datasets/test_red_blood_cell.vtk' - ) - mesh_points[0][0] = 2.2 - mesh_points[5][1] = 4.3 - mesh_points[9][2] = 0.5 - mesh_points[45][0] = 7.2 - mesh_points[132][1] = -1.2 - mesh_points[255][2] = -3.6 - - outfilename = 'tests/test_datasets/test_red_blood_cell_out.vtk' - if vtk.VTK_MAJOR_VERSION <= 5: - outfilename_expected = 'tests/test_datasets/test_red_blood_cell_out_true_version5.vtk' - else: - outfilename_expected = 'tests/test_datasets/test_red_blood_cell_out_true_version6.vtk' - - vtk_handler.write(mesh_points, outfilename) - self.assertTrue(self.cmp(outfilename, outfilename_expected)) - os.remove(outfilename) - - def test_vtk_plot_failing_outfile_type(self): - vtk_handler = vh.VtkHandler() - with self.assertRaises(TypeError): - vtk_handler.plot(plot_file=1.1) - - def test_vtk_plot_save_fig_infile(self): - vtk_handler = vh.VtkHandler() - mesh_points = vtk_handler.parse( - 'tests/test_datasets/test_red_blood_cell.vtk' - ) - vtk_handler.plot(save_fig=True) - self.assertTrue( - os.path.isfile('tests/test_datasets/test_red_blood_cell.png') - ) - os.remove('tests/test_datasets/test_red_blood_cell.png') - - def test_vtk_plot_save_fig_plot_file(self): - vtk_handler = vh.VtkHandler() - vtk_handler.plot( - plot_file='tests/test_datasets/test_red_blood_cell.vtk', - save_fig=True - ) - self.assertTrue( - os.path.isfile('tests/test_datasets/test_red_blood_cell.png') - ) - os.remove('tests/test_datasets/test_red_blood_cell.png') - - def test_vtk_show_failing_outfile_type(self): - vtk_handler = vh.VtkHandler() - with self.assertRaises(TypeError): - vtk_handler.show(show_file=1.1) + def cmp(self, f1, f2): + """ + Check if the two files have the same content, skipping comment lines + """ + content1 = [line for line in open(f1) if not line.startswith('#')] + content2 = [line for line in open(f1) if not line.startswith('#')] + return content1 == content2 + + def test_vtk_instantiation(self): + vtk_handler = vh.VtkHandler() + + def test_vtk_default_infile_member(self): + vtk_handler = vh.VtkHandler() + assert vtk_handler.infile == None + + def test_vtk_default_outfile_member(self): + vtk_handler = vh.VtkHandler() + assert vtk_handler.outfile == None + + def test_vtk_default_extension_member(self): + vtk_handler = vh.VtkHandler() + self.assertListEqual(vtk_handler.extensions, ['.vtk']) + + def test_vtk_parse_failing_filename_type(self): + vtk_handler = vh.VtkHandler() + with self.assertRaises(TypeError): + mesh_points = vtk_handler.parse(5.2) + + def test_vtk_parse_failing_check_extension(self): + vtk_handler = vh.VtkHandler() + with self.assertRaises(ValueError): + mesh_points = vtk_handler.parse( + 'tests/test_datasets/test_square.iges') + + def test_vtk_parse_infile(self): + vtk_handler = vh.VtkHandler() + mesh_points = vtk_handler.parse( + 'tests/test_datasets/test_red_blood_cell.vtk') + assert vtk_handler.infile == 'tests/test_datasets/test_red_blood_cell.vtk' + + def test_vtk_parse_shape(self): + vtk_handler = vh.VtkHandler() + mesh_points = vtk_handler.parse( + 'tests/test_datasets/test_red_blood_cell.vtk') + assert mesh_points.shape == (500, 3) + + def test_vtk_parse_coords_1(self): + vtk_handler = vh.VtkHandler() + mesh_points = vtk_handler.parse( + 'tests/test_datasets/test_red_blood_cell.vtk') + np.testing.assert_almost_equal(mesh_points[33][0], -2.2977099) + + def test_vtk_parse_coords_2(self): + vtk_handler = vh.VtkHandler() + mesh_points = vtk_handler.parse( + 'tests/test_datasets/test_red_blood_cell.vtk') + np.testing.assert_almost_equal(mesh_points[178][1], 0.143506) + + def test_vtk_parse_coords_3(self): + vtk_handler = vh.VtkHandler() + mesh_points = vtk_handler.parse( + 'tests/test_datasets/test_red_blood_cell.vtk') + np.testing.assert_almost_equal(mesh_points[100][2], 2.3306999) + + def test_vtk_parse_coords_4(self): + vtk_handler = vh.VtkHandler() + mesh_points = vtk_handler.parse( + 'tests/test_datasets/test_red_blood_cell.vtk') + np.testing.assert_almost_equal(mesh_points[0][0], -3.42499995) + + def test_vtk_parse_coords_5(self): + vtk_handler = vh.VtkHandler() + mesh_points = vtk_handler.parse( + 'tests/test_datasets/test_red_blood_cell.vtk') + np.testing.assert_almost_equal(mesh_points[-1][2], -2.8480699) + + def test_vtk_write_failing_filename_type(self): + vtk_handler = vh.VtkHandler() + mesh_points = vtk_handler.parse( + 'tests/test_datasets/test_red_blood_cell.vtk') + with self.assertRaises(TypeError): + vtk_handler.write(mesh_points, -2) + + def test_vtk_write_failing_check_extension(self): + vtk_handler = vh.VtkHandler() + mesh_points = vtk_handler.parse( + 'tests/test_datasets/test_red_blood_cell.vtk') + with self.assertRaises(ValueError): + vtk_handler.write(mesh_points, + 'tests/test_datasets/test_square.iges') + + def test_vtk_write_failing_infile_instantiation(self): + vtk_handler = vh.VtkHandler() + mesh_points = np.zeros((20, 3)) + with self.assertRaises(RuntimeError): + vtk_handler.write(mesh_points, + 'tests/test_datasets/test_red_blood_cell_out.vtk') + + def test_vtk_write_outfile(self): + vtk_handler = vh.VtkHandler() + mesh_points = vtk_handler.parse( + 'tests/test_datasets/test_red_blood_cell.vtk') + outfilename = 'tests/test_datasets/test_red_blood_cell_out.vtk' + vtk_handler.write(mesh_points, outfilename) + assert vtk_handler.outfile == outfilename + os.remove(outfilename) + + def test_vtk_write_comparison(self): + import vtk + vtk_handler = vh.VtkHandler() + mesh_points = vtk_handler.parse( + 'tests/test_datasets/test_red_blood_cell.vtk') + mesh_points[0][0] = 2.2 + mesh_points[5][1] = 4.3 + mesh_points[9][2] = 0.5 + mesh_points[45][0] = 7.2 + mesh_points[132][1] = -1.2 + mesh_points[255][2] = -3.6 + + outfilename = 'tests/test_datasets/test_red_blood_cell_out.vtk' + if vtk.VTK_MAJOR_VERSION <= 5: + outfilename_expected = 'tests/test_datasets/test_red_blood_cell_out_true_version5.vtk' + else: + outfilename_expected = 'tests/test_datasets/test_red_blood_cell_out_true_version6.vtk' + + vtk_handler.write(mesh_points, outfilename) + self.assertTrue(self.cmp(outfilename, outfilename_expected)) + os.remove(outfilename) + + def test_vtk_plot_failing_outfile_type(self): + vtk_handler = vh.VtkHandler() + with self.assertRaises(TypeError): + vtk_handler.plot(plot_file=1.1) + + def test_vtk_plot_save_fig_infile(self): + vtk_handler = vh.VtkHandler() + mesh_points = vtk_handler.parse( + 'tests/test_datasets/test_red_blood_cell.vtk') + vtk_handler.plot(save_fig=True) + self.assertTrue( + os.path.isfile('tests/test_datasets/test_red_blood_cell.png')) + os.remove('tests/test_datasets/test_red_blood_cell.png') + + def test_vtk_plot_save_fig_plot_file(self): + vtk_handler = vh.VtkHandler() + vtk_handler.plot( + plot_file='tests/test_datasets/test_red_blood_cell.vtk', + save_fig=True) + self.assertTrue( + os.path.isfile('tests/test_datasets/test_red_blood_cell.png')) + os.remove('tests/test_datasets/test_red_blood_cell.png') + + def test_vtk_show_failing_outfile_type(self): + vtk_handler = vh.VtkHandler() + with self.assertRaises(TypeError): + vtk_handler.show(show_file=1.1)