diff --git a/docs/source/params.rst b/docs/source/params.rst index 932aabe..2da5382 100644 --- a/docs/source/params.rst +++ b/docs/source/params.rst @@ -6,3 +6,7 @@ Params .. autoclass:: FFDParameters :members: :private-members: + +.. autoclass:: RBFParameters + :members: + :private-members: \ No newline at end of file diff --git a/pygem/params.py b/pygem/params.py index a475531..d06f908 100644 --- a/pygem/params.py +++ b/pygem/params.py @@ -1,5 +1,5 @@ """ -Utilities for reading and writing parameters files to perform the Free Form Deformation (FFD) +Utilities for reading and writing parameters files to perform the desired geometrical morphing. """ import os import ConfigParser @@ -268,13 +268,11 @@ def print_info(self): """ print 'conversion_unit = ' + str(self.conversion_unit) + '\n' print '(lenght_box_x, lenght_box_y, lenght_box_z) = (' + str(self.lenght_box_x) + \ - ', ' + str(self.lenght_box_y) + ', ' + \ - str(self.lenght_box_z) + ')' + ', ' + str(self.lenght_box_y) + ', ' + str(self.lenght_box_z) + ')' print 'origin_box = ' + str(self.origin_box) print 'n_control_points = ' + str(self.n_control_points) print '(rot_angle_x, rot_angle_y, rot_angle_z) = (' + str(self.rot_angle_x) + \ - ', ' + str(self.rot_angle_y) + ', ' + \ - str(self.rot_angle_z) + ')' + ', ' + str(self.rot_angle_y) + ', ' + str(self.rot_angle_z) + ')' print '\narray_mu_x =' print self.array_mu_x print '\narray_mu_y =' @@ -297,3 +295,94 @@ def print_info(self): print self.position_vertex_3 + +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. + 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.n_control_points = None + self.original_control_points = None + self.deformed_control_points = None + + + 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, basestring): + 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') + + 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, 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, 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 print_info(self): + """ + This method prints all the RBF parameters on the screen. Its purpose is for debugging. + """ + print 'basis function = ' + str(self.basis) + print 'radius = ' + str(self.radius) + print 'n_control_points = ' + str(self.n_control_points) + print '\noriginal_control_points =' + print self.original_control_points + print '\ndeformed_control_points =' + print self.deformed_control_points + diff --git a/pygem/stlhandler.py b/pygem/stlhandler.py index 35ffb3a..4561b3e 100644 --- a/pygem/stlhandler.py +++ b/pygem/stlhandler.py @@ -4,7 +4,7 @@ import numpy as np from mpl_toolkits import mplot3d from matplotlib import pyplot -from stl import mesh +from stl import mesh, Mode import pygem.filehandler as fh @@ -49,9 +49,9 @@ def parse(self, filename): def write(self, mesh_points, filename, write_bin=False): """ - Writes a unv file, called filename, copying all the lines from self.filename but + 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 unv file. + 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. @@ -75,7 +75,7 @@ def write(self, mesh_points, filename, write_bin=False): data['vectors'][i][j] = mesh_points[3*i + j] if not write_bin: - stl_mesh.save(self.outfile, mode=1, update_normals=True) + stl_mesh.save(self.outfile, mode=Mode.ASCII, update_normals=True) else: stl_mesh.save(self.outfile, update_normals=True) diff --git a/tests/test_datasets/parameters_rbf_bugged_01.prm b/tests/test_datasets/parameters_rbf_bugged_01.prm new file mode 100644 index 0000000..2630849 --- /dev/null +++ b/tests/test_datasets/parameters_rbf_bugged_01.prm @@ -0,0 +1,37 @@ + +[Radial Basis Functions] +# This section describes the radial basis functions shape. + +# 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. +# For a comprehensive list with details see the class RBF. +basis function: gaussian_spline + +# radius is the scaling parameter r that affects the shape of the basis functions. See the documentation +# of the class RBF for details. +radius: 0.5 + +[Control points] +# This section describes the RBF control points. + +# original control points collects the coordinates of the interpolation control points before the deformation. +original control points: 0.0 0.0 0.0 + 0.0 0.0 1.0 + 0.0 1.0 0.0 + 1.0 0.0 0.0 + 0.0 1.0 1.0 + 1.0 0.0 1.0 + 1.0 1.0 0.0 + 1.0 1.0 1.0 + +# deformed control points collects the coordinates of the interpolation control points after the deformation. +deformed control points: 0.0 0.0 0.0 + 0.0 0.0 1.0 + 0.0 1.0 0.0 + 1.0 0.0 0.0 + 0.0 1.0 1.0 + 1.0 0.0 1.0 + 1.0 1.0 0.0 + 1.0 1.0 1.0 + 0.5 0.5 0.5 diff --git a/tests/test_datasets/parameters_rbf_bugged_02.prm b/tests/test_datasets/parameters_rbf_bugged_02.prm new file mode 100644 index 0000000..906bd23 --- /dev/null +++ b/tests/test_datasets/parameters_rbf_bugged_02.prm @@ -0,0 +1,36 @@ + +[Radial Basis Functions] +# This section describes the radial basis functions shape. + +# 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. +# For a comprehensive list with details see the class RBF. +basis function: gaussian_splines + +# radius is the scaling parameter r that affects the shape of the basis functions. See the documentation +# of the class RBF for details. +radius: 0.5 + +[Control points] +# This section describes the RBF control points. + +# original control points collects the coordinates of the interpolation control points before the deformation. +original control points: 0.0 0.0 0.0 + 0.0 0.0 1.0 + 0.0 1.0 0.0 + 1.0 0.0 0.0 + 0.0 1.0 1.0 + 1.0 0.0 1.0 + 1.0 1.0 0.0 + 1.0 1.0 1.0 + +# deformed control points collects the coordinates of the interpolation control points after the deformation. +deformed control points: 0.0 0.0 0.0 + 0.0 0.0 1.0 + 0.0 1.0 0.0 + 1.0 0.0 0.0 + 0.0 1.0 1.0 + 1.0 0.0 1.0 + 1.0 1.0 0.0 + 1.0 1.0 1.0 diff --git a/tests/test_datasets/parameters_rbf_default.prm b/tests/test_datasets/parameters_rbf_default.prm new file mode 100644 index 0000000..4eabf5c --- /dev/null +++ b/tests/test_datasets/parameters_rbf_default.prm @@ -0,0 +1,36 @@ + +[Radial Basis Functions] +# This section describes the radial basis functions shape. + +# 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. +# For a comprehensive list with details see the class RBF. +basis function: gaussian_spline + +# radius is the scaling parameter r that affects the shape of the basis functions. See the documentation +# of the class RBF for details. +radius: 0.5 + +[Control points] +# This section describes the RBF control points. + +# original control points collects the coordinates of the interpolation control points before the deformation. +original control points: 0.0 0.0 0.0 + 0.0 0.0 1.0 + 0.0 1.0 0.0 + 1.0 0.0 0.0 + 0.0 1.0 1.0 + 1.0 0.0 1.0 + 1.0 1.0 0.0 + 1.0 1.0 1.0 + +# deformed control points collects the coordinates of the interpolation control points after the deformation. +deformed control points: 0.0 0.0 0.0 + 0.0 0.0 1.0 + 0.0 1.0 0.0 + 1.0 0.0 0.0 + 0.0 1.0 1.0 + 1.0 0.0 1.0 + 1.0 1.0 0.0 + 1.0 1.0 1.0 diff --git a/tests/test_params.py b/tests/test_ffdparams.py similarity index 100% rename from tests/test_params.py rename to tests/test_ffdparams.py diff --git a/tests/test_rbfparams.py b/tests/test_rbfparams.py new file mode 100644 index 0000000..aedb14e --- /dev/null +++ b/tests/test_rbfparams.py @@ -0,0 +1,86 @@ + +from unittest import TestCase +import unittest +import pygem.params as rbfp +import numpy as np +import filecmp +import os + + +class TestFFDParameters(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_print_info(self): + params = rbfp.RBFParameters() + params.print_info()