From 3e897b8cb911304e9fcda0f045f1acf5d85a5a7c Mon Sep 17 00:00:00 2001 From: davide baroli Date: Tue, 11 Oct 2016 21:25:07 +0200 Subject: [PATCH 1/8] add dockerfile; update README; update travis.ylm --- .travis.yml | 16 ++++-- README.md | 27 +++++++++ dockerfiles/Dockerfile | 86 +++++++++++++++++++++++++++++ dockerfiles/set-home-permissions.sh | 17 ++++++ 4 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 dockerfiles/Dockerfile create mode 100644 dockerfiles/set-home-permissions.sh diff --git a/.travis.yml b/.travis.yml index 55da4db..9ddbf47 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,10 @@ - +sudo: true +dist: trusty + language: python +service: docker + matrix: include: - os: linux @@ -43,7 +47,7 @@ install: conda create --yes -n test python="2.7"; fi - source activate test - - conda install --yes numpy scipy matplotlib pip nose vtk + - conda install --yes numpy scipy matplotlib pip nose vtk sip=4.18 - conda install --yes -c https://conda.anaconda.org/dlr-sc pythonocc-core - pip install setuptools - pip install enum34 @@ -52,8 +56,12 @@ install: - pip install coverage - python setup.py install -script: coverage run test.py - +script: + - coverage run test.py + # Docker in travis works only with linux. + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + docker run -u root docker.io/pygemdocker/pygem:latest /bin/sh -c "cd /home/PyGeM/build/PyGeM; coverage run test.py"; + fi after_success: - coveralls diff --git a/README.md b/README.md index 8773887..5a4497d 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,34 @@ To uninstall the package you have to rerun the installation and record the insta > python setup.py install --record installed_files.txt > cat installed_files.txt | xargs rm -rf ``` +Alternatively, a way to run the PyGeM library is to use our prebuilt and high-performance Docker images. +Docker containers are extremely lightweight, secure, and are based on open standards that run on all major Linux distributions, macOS and Microsoft Windows platforms. +Install Docker for your platform by following [these instructions](https://docs.docker.com/engine/getstarted/step_one/). +If using the Docker Toolbox (macOS versions < 10.10 or Windows versions < 10), make sure you run all commands inside the Docker Quickstart Terminal. + +Now we will pull the docker.io/pygemdocker/pygem image from our cloud infrastructure: +```bash +> docker pull docker.io/pygemdocker/pygem:latest +``` +Docker will pull the latest tag of the image pygemdocker/pygem from docker.io. The download is around 3.246 GB. The image is a great place to start experimenting with PyGeM and includes all dependencies already compiled for you. +Once the download is complete you can start PyGeM for the first time. Just run: +```bash +> docker run -ti pygemdocker/pygem:latest +``` +To facilitate the devoloping, using the text editor,version control and other tools already installed on your computers, +it is possible to share files from the host into the container: + +```bash +> docker run -ti -v $(pwd):/home/PyGeM/shared pygemdocker/pygem:latest +``` +To allow the X11 forwarding in the container, on Linux system just run: + +```bash +> docker run -ti --rm -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix -v $(pwd):/home/PyGeM/shared pygemdocker/pygem:latest +``` + +For Windows system, you need to install Cygwin/X version and running the command in Cygwin terminal. While for mac system, you need to install xquartz. ## Documentation **PyGeM** uses [Sphinx](http://www.sphinx-doc.org/en/stable/) for code documentation. To build the html versions of the docs simply: diff --git a/dockerfiles/Dockerfile b/dockerfiles/Dockerfile new file mode 100644 index 0000000..f67b9c9 --- /dev/null +++ b/dockerfiles/Dockerfile @@ -0,0 +1,86 @@ +FROM phusion/baseimage:0.9.19 + +# Get Ubuntu updates +USER root +RUN apt-get update -q && \ + apt-get upgrade -y -o Dpkg::Options::="--force-confold" && \ + apt-get -y install sudo && \ + apt-get -y install locales && \ + echo "C.UTF-8 UTF-8" > /etc/locale.gen && \ + locale-gen && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +# Set locale environment +ENV LC_ALL=C.UTF-8 \ + LANG=C.UTF-8 \ + LANGUAGE=C.UTF-8 + +# OpenBLAS threads should be 1 to ensure performance +RUN echo 1 > /etc/container_environment/OPENBLAS_NUM_THREADS && \ + echo 0 > /etc/container_environment/OPENBLAS_VERBOSE + + +# Set up user so that we do not run as root +RUN useradd -m -s /bin/bash -G sudo,docker_env PyGeM && \ + echo "PyGeM:docker" | chpasswd && \ + echo "PyGeM ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + +RUN touch /etc/service/syslog-forwarder/down +COPY set-home-permissions.sh /etc/my_init.d/set-home-permissions.sh +RUN chmod +x /etc/my_init.d/set-home-permissions.sh + +USER PyGeM +ENV HOME /home/PyGeM +RUN touch $HOME/.sudo_as_admin_successful && \ + mkdir $HOME/shared && \ + mkdir $HOME/build +VOLUME /home/PyGeM/shared + +WORKDIR /home/PyGeM +ENTRYPOINT ["sudo","/sbin/my_init","--quiet","--","sudo","-u","PyGeM","/bin/bash","-l","-c"] +CMD ["/bin/bash","-i"] + +# utilities and libraries +USER root +RUN apt-get update -y; apt-get install -y --force-yes --fix-missing --no-install-recommends curl git unzip tree subversion vim cmake bison g++ gfortran openmpi-bin pkg-config wget libpcre3-dev bison flex swig libglu1-mesa pyqt4-dev-tools +RUN apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN id PyGeM +RUN chown -R PyGeM:PyGeM $HOME + +RUN cd /tmp && \ + wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh && \ + chmod +x miniconda.sh && \ + bash miniconda.sh -b -p /usr/local/miniconda && \ + rm /tmp/* +ENV PATH=/usr/local/miniconda/bin:$PATH + +RUN echo "PATH=/usr/local/miniconda/bin:$PATH" >> ~/.profile +RUN /bin/bash -c 'source ~/.profile' + +RUN hash -r && \ + conda config --set always_yes yes --set changeps1 no && \ + conda update -q conda +RUN conda info -a && \ + conda create --yes -n test python="2.7"; + +RUN /bin/bash -c 'source activate test' +# The default sip version has api that is not compatible with qt4. +RUN conda install --yes numpy scipy matplotlib pip nose vtk sip=4.18 +RUN conda install --yes -c https://conda.anaconda.org/dlr-sc pythonocc-core &&\ + pip install setuptools && \ + pip install enum34 && \ + pip install numpy-stl && \ + pip install coveralls && \ + pip install coverage + +RUN cd $HOME && \ + cd build && \ + git clone https://github.com/mathLab/PyGeM.git && \ + cd PyGeM && \ + python setup.py install + +USER PyGeM + + diff --git a/dockerfiles/set-home-permissions.sh b/dockerfiles/set-home-permissions.sh new file mode 100644 index 0000000..7e3c6d8 --- /dev/null +++ b/dockerfiles/set-home-permissions.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# User can pass e.g. --env HOST_UID=1003 so that UID in the container matches +# with the UID on the host. This is useful for Linux users, Mac and Windows +# already do transparent mapping of shared volumes. +if [ "$HOST_UID" ]; then + usermod -u $HOST_UID PyGeM +fi +if [ "$HOST_GID" ]; then + groupmod -g $HOST_GID PyGeM +fi +# This makes sure that all files in /home/fenics are accessible by the user +# fenics. We exclude the folder ~/shared to reduce IO out to the host. Docker +# for Mac, Docker for Windows and the UID/GID trick above should mean that file +# permissions work seamlessly now. +cd /home/PyGeM +find . -maxdepth 1 | grep -v "./shared" | xargs chown -R PyGeM:PyGeM + From 563404a44ae285a55420db56bdb3cdb3d29e09e8 Mon Sep 17 00:00:00 2001 From: Maciej Grochowicz Date: Mon, 17 Oct 2016 13:20:45 +0200 Subject: [PATCH 2/8] Add bounding box params --- pygem/params.py | 922 ++++++++++++++++++++++------------------ tests/test_ffdparams.py | 722 +++++++++++++++++-------------- 2 files changed, 900 insertions(+), 744 deletions(-) diff --git a/pygem/params.py b/pygem/params.py index f237de2..d2be80e 100644 --- a/pygem/params.py +++ b/pygem/params.py @@ -1,438 +1,516 @@ """ Utilities for reading and writing parameters files to perform the desired geometrical morphing. """ -import os import ConfigParser +import os + import numpy as np +from OCC.BRepBndLib import brepbndlib_Add +from OCC.BRepMesh import BRepMesh_IncrementalMesh +from OCC.Bnd import Bnd_Box + import pygem.affine as at 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: - - >>> 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 defaul paramters 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') - - .. 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. + """ + Class that handles the Free Form Deformation parameters in terms of FFD bounding box and + weight of the FFD control points. + + :param x_ctrl_pnts (int): Number of control points along OX axis + :param y_ctrl_pnts (int): Number of control points along OY axis + :param z_ctrl_pnts (int): Number of control points along OZ axis + + :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: + + >>> import pygem.params as ffdp + + >>> # Reading an existing file + >>> params1 = ffdp.FFDParameters() + >>> params1.read_parameters(filename='tests/test_datasets/parameters_test_ffd_identity.prm') + + .. 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_x = 1. - self.lenght_box_y = 1. - self.lenght_box_z = 1. - - self.origin_box = np.array([0., 0., 0.]) - - self.rot_angle_x = 0. - self.rot_angle_y = 0. - self.rot_angle_z = 0. - - if n_control_points is None: - n_control_points = [2, 2, 2] - self.n_control_points = n_control_points - - self.array_mu_x = np.zeros((self.n_control_points[0], self.n_control_points[1], \ - self.n_control_points[2])) - self.array_mu_y = np.zeros((self.n_control_points[0], self.n_control_points[1], \ - self.n_control_points[2])) - self.array_mu_z = np.zeros((self.n_control_points[0], self.n_control_points[1], \ - self.n_control_points[2])) - - self.psi_mapping = np.diag([1./self.lenght_box_x, 1./self.lenght_box_y, 1./self.lenght_box_z]) - self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) - - 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, basestring): - 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_x = config.getfloat('Box info', 'box lenght x') - self.lenght_box_y = config.getfloat('Box info', 'box lenght y') - self.lenght_box_z = config.getfloat('Box info', 'box lenght z') - - origin_box_x = config.getfloat('Box info', 'box origin x') - origin_box_y = config.getfloat('Box info', 'box origin y') - origin_box_z = config.getfloat('Box info', 'box origin z') - self.origin_box = np.array([origin_box_x, origin_box_y, origin_box_z]) - - self.rot_angle_x = config.getfloat('Box info', 'rotation angle x') - self.rot_angle_y = config.getfloat('Box info', 'rotation angle y') - self.rot_angle_z = config.getfloat('Box info', 'rotation angle z') - - self.array_mu_x = np.zeros((self.n_control_points[0], self.n_control_points[1], \ - self.n_control_points[2])) - mux = config.get('Parameters weights', 'parameter x') - lines = mux.split('\n') - for line in lines: - values = line.split() - self.array_mu_x[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - - self.array_mu_y = np.zeros((self.n_control_points[0], self.n_control_points[1], \ - self.n_control_points[2])) - muy = config.get('Parameters weights', 'parameter y') - lines = muy.split('\n') - for line in lines: - values = line.split() - self.array_mu_y[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - - self.array_mu_z = np.zeros((self.n_control_points[0], self.n_control_points[1], \ - self.n_control_points[2])) - muz = config.get('Parameters weights', 'parameter z') - lines = muz.split('\n') - for line in lines: - values = line.split() - self.array_mu_z[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - - self.rotation_matrix = at.angles2matrix(self.rot_angle_z * np.pi/180, \ - self.rot_angle_y * np.pi/180, self.rot_angle_x * np.pi/180) - - self.position_vertex_0 = self.origin_box - self.position_vertex_1 = self.position_vertex_0 + \ - np.dot(self.rotation_matrix, [self.lenght_box_x, 0, 0]) - self.position_vertex_2 = self.position_vertex_0 + \ - np.dot(self.rotation_matrix, [0, self.lenght_box_y, 0]) - self.position_vertex_3 = self.position_vertex_0 + \ - np.dot(self.rotation_matrix, [0, 0, self.lenght_box_z]) - - self.psi_mapping = np.diag([1./self.lenght_box_x, 1./self.lenght_box_y, 1./self.lenght_box_z]) - self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) - - - 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, basestring): - raise TypeError("filename must be a string") - - with open(filename, 'w') as output_file: - output_file.write('\n[Box info]\n') - output_file.write('# This section collects all the properties of the FFD bounding box.\n') - - output_file.write('\n# n control points indicates the number of control points in each direction (x, y, z).\n') - output_file.write('# For example, to create a 2 x 3 x 2 grid, use the following: n control points: 2, 3, 2\n') - output_file.write('n control points x: ' + str(self.n_control_points[0]) + '\n') - output_file.write('n control points y: ' + str(self.n_control_points[1]) + '\n') - output_file.write('n control points z: ' + str(self.n_control_points[2]) + '\n') - - output_file.write('\n# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).\n') - output_file.write('# It uses the local coordinate system.\n') - output_file.write('# 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_file.write('box lenght x: ' + str(self.lenght_box_x) + '\n') - output_file.write('box lenght y: ' + str(self.lenght_box_y) + '\n') - output_file.write('box lenght z: ' + str(self.lenght_box_z) + '\n') - - output_file.write('\n# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of\n') - output_file.write('# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].\n') - output_file.write('# See section "Parameters weights" for more details.\n') - output_file.write('# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.\n') - output_file.write('box origin x: ' + str(self.origin_box[0]) + '\n') - output_file.write('box origin y: ' + str(self.origin_box[1]) + '\n') - output_file.write('box origin z: ' + str(self.origin_box[2]) + '\n') - - output_file.write('\n# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.\n') - output_file.write('# The rotation is done with respect to the box origin.\n') - output_file.write('# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.\n') - output_file.write('rotation angle x: ' + str(self.rot_angle_x) + '\n') - output_file.write('rotation angle y: ' + str(self.rot_angle_y) + '\n') - output_file.write('rotation angle z: ' + str(self.rot_angle_z) + '\n') - - output_file.write('\n\n[Parameters weights]\n') - output_file.write('# This section describes the weights of the FFD control points.\n') - output_file.write('# We adopt the following convention:\n') - output_file.write('# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.\n') - output_file.write('# If a weight is equal to zero you can discard the line since the default is zero.\n') - output_file.write('#\n') - output_file.write('# | x index | y index | z index | weight |\n') - output_file.write('# --------------------------------------\n') - output_file.write('# | 0 | 0 | 0 | 1.0 |\n') - output_file.write('# | 0 | 1 | 1 | 0.0 | --> you can erase this line without effects\n') - output_file.write('# | 0 | 1 | 0 | -2.1 |\n') - output_file.write('# | 0 | 0 | 1 | 3.4 |\n') - - output_file.write('\n# parameter x collects the displacements along x, normalized with the box lenght x.') - output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_x[i][j][k]) + '\n') - offset = 13 - - output_file.write('\n# parameter y collects the displacements along y, normalized with the box lenght y.') - output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_y[i][j][k]) + '\n') - offset = 13 - - output_file.write('\n# parameter z collects the displacements along z, normalized with the box lenght z.') - output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_z[i][j][k]) + '\n') - offset = 13 - - - def print_info(self): - """ - This method prints all the FFD parameters on the screen. Its purpose is for debugging. - """ - 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) + ')' - 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) + ')' - print '\narray_mu_x =' - print self.array_mu_x - print '\narray_mu_y =' - print self.array_mu_y - print '\narray_mu_z =' - print self.array_mu_z - print '\npsi_mapping =' - print self.psi_mapping - print '\ninv_psi_mapping =' - print self.inv_psi_mapping - print '\nrotation_matrix =' - print self.rotation_matrix - print '\nposition_vertex_0 =' - print self.position_vertex_0 - print '\nposition_vertex_1 =' - print self.position_vertex_1 - print '\nposition_vertex_2 =' - print self.position_vertex_2 - print '\nposition_vertex_3 =' - print self.position_vertex_3 + def __init__(self, n_control_points=None): + self.conversion_unit = 1. + + self.lenght_box_x = 1. + self.lenght_box_y = 1. + self.lenght_box_z = 1. + + self.origin_box = np.array([0., 0., 0.]) + + self.rot_angle_x = 0. + self.rot_angle_y = 0. + self.rot_angle_z = 0. + + if n_control_points is None: + self.n_control_points = [2, 2, 2] + else: + self.n_control_points = n_control_points + + self._set_transformation_params_to_zero() + + self.psi_mapping = np.diag([1. / self.lenght_box_x, 1. / self.lenght_box_y, 1. / self.lenght_box_z]) + self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) + + 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.]) + self.array_mu_x = np.zeros(shape=self.n_control_points) + self.array_mu_y = np.zeros(shape=self.n_control_points) + self.array_mu_z = np.zeros(shape=self.n_control_points) + + 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. + 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_x = config.getfloat('Box info', 'box lenght x') + self.lenght_box_y = config.getfloat('Box info', 'box lenght y') + self.lenght_box_z = config.getfloat('Box info', 'box lenght z') + + origin_box_x = config.getfloat('Box info', 'box origin x') + origin_box_y = config.getfloat('Box info', 'box origin y') + origin_box_z = config.getfloat('Box info', 'box origin z') + self.origin_box = np.array([origin_box_x, origin_box_y, origin_box_z]) + + self.rot_angle_x = config.getfloat('Box info', 'rotation angle x') + self.rot_angle_y = config.getfloat('Box info', 'rotation angle y') + self.rot_angle_z = config.getfloat('Box info', 'rotation angle z') + + self.array_mu_x = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + mux = config.get('Parameters weights', 'parameter x') + lines = mux.split('\n') + for line in lines: + values = line.split() + self.array_mu_x[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) + + self.array_mu_y = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + muy = config.get('Parameters weights', 'parameter y') + lines = muy.split('\n') + for line in lines: + values = line.split() + self.array_mu_y[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) + + self.array_mu_z = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + muz = config.get('Parameters weights', 'parameter z') + lines = muz.split('\n') + for line in lines: + values = line.split() + self.array_mu_z[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) + + self.rotation_matrix = at.angles2matrix( + self.rot_angle_z * np.pi / 180, + self.rot_angle_y * np.pi / 180, + self.rot_angle_x * np.pi / 180 + ) + + self.position_vertex_0 = self.origin_box + self.position_vertex_1 = self.position_vertex_0 + np.dot(self.rotation_matrix, [self.lenght_box_x, 0, 0]) + self.position_vertex_2 = self.position_vertex_0 + np.dot(self.rotation_matrix, [0, self.lenght_box_y, 0]) + self.position_vertex_3 = self.position_vertex_0 + np.dot(self.rotation_matrix, [0, 0, self.lenght_box_z]) + + self.psi_mapping = np.diag([1. / self.lenght_box_x, 1. / self.lenght_box_y, 1. / self.lenght_box_z]) + self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) + + 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, basestring): + raise TypeError("filename must be a string") + + with open(filename, 'w') as output_file: + output_file.write('\n[Box info]\n') + output_file.write('# This section collects all the properties of the FFD bounding box.\n') + + output_file.write( + '\n# n control points indicates the number of control points in each direction (x, y, z).\n') + output_file.write( + '# For example, to create a 2 x 3 x 2 grid, use the following: n control points: 2, 3, 2\n') + output_file.write('n control points x: ' + str(self.n_control_points[0]) + '\n') + output_file.write('n control points y: ' + str(self.n_control_points[1]) + '\n') + output_file.write('n control points z: ' + str(self.n_control_points[2]) + '\n') + + output_file.write( + '\n# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).\n') + output_file.write('# It uses the local coordinate system.\n') + output_file.write( + '# 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_file.write('box lenght x: ' + str(self.lenght_box_x) + '\n') + output_file.write('box lenght y: ' + str(self.lenght_box_y) + '\n') + output_file.write('box lenght z: ' + str(self.lenght_box_z) + '\n') + + output_file.write( + '\n# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of\n') + output_file.write( + '# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].\n') + output_file.write('# See section "Parameters weights" for more details.\n') + output_file.write( + '# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.\n') + output_file.write('box origin x: ' + str(self.origin_box[0]) + '\n') + output_file.write('box origin y: ' + str(self.origin_box[1]) + '\n') + output_file.write('box origin z: ' + str(self.origin_box[2]) + '\n') + + output_file.write( + '\n# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.\n') + output_file.write('# The rotation is done with respect to the box origin.\n') + output_file.write( + '# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.\n') + output_file.write('rotation angle x: ' + str(self.rot_angle_x) + '\n') + output_file.write('rotation angle y: ' + str(self.rot_angle_y) + '\n') + output_file.write('rotation angle z: ' + str(self.rot_angle_z) + '\n') + + output_file.write('\n\n[Parameters weights]\n') + output_file.write('# This section describes the weights of the FFD control points.\n') + output_file.write('# We adopt the following convention:\n') + output_file.write( + '# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.\n') + output_file.write('# If a weight is equal to zero you can discard the line since the default is zero.\n') + output_file.write('#\n') + output_file.write('# | x index | y index | z index | weight |\n') + output_file.write('# --------------------------------------\n') + output_file.write('# | 0 | 0 | 0 | 1.0 |\n') + output_file.write( + '# | 0 | 1 | 1 | 0.0 | --> you can erase this line without effects\n') + output_file.write('# | 0 | 1 | 0 | -2.1 |\n') + output_file.write('# | 0 | 0 | 1 | 3.4 |\n') + + output_file.write('\n# parameter x collects the displacements along x, normalized with the box lenght x.') + output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ + ' ' + str(self.array_mu_x[i][j][k]) + '\n') + offset = 13 + + output_file.write('\n# parameter y collects the displacements along y, normalized with the box lenght y.') + output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ + ' ' + str(self.array_mu_y[i][j][k]) + '\n') + offset = 13 + + output_file.write('\n# parameter z collects the displacements along z, normalized with the box lenght z.') + output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ + ' ' + str(self.array_mu_z[i][j][k]) + '\n') + offset = 13 + + def print_info(self): + """ + This method prints all the FFD parameters on the screen. Its purpose is for debugging. + """ + 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) + ')' + 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) + ')' + print '\narray_mu_x =' + print self.array_mu_x + print '\narray_mu_y =' + print self.array_mu_y + print '\narray_mu_z =' + print self.array_mu_z + print '\npsi_mapping =' + print self.psi_mapping + print '\ninv_psi_mapping =' + print self.inv_psi_mapping + print '\nrotation_matrix =' + print self.rotation_matrix + print '\nposition_vertex_0 =' + print self.position_vertex_0 + print '\nposition_vertex_1 =' + print self.position_vertex_1 + print '\nposition_vertex_2 =' + print self.position_vertex_2 + print '\nposition_vertex_3 =' + print self.position_vertex_3 + + def build_bounding_box(self, shape, tol=1e-6, triangualte=False, triangulate_tol=1e-1): + min_xyz, max_xyz = self._calculate_bb_dimension(shape, tol, triangualte, 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_origin(self, xyz): + self.origin_box = xyz + + 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. + :return: + """ + dims = [max_xyz[i] - min_xyz[i] for i in range(3)] + self.lenght_box_x = dims[0] + self.lenght_box_y = dims[1] + self.lenght_box_z = dims[2] + + def _set_position_of_vertices(self): + """ + Vertices of the control box around the object are set in this method. + Four vertex (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. + :return: + """ + origin_array = np.array(self.origin_box) + dim = [self.lenght_box_x, self.lenght_box_y, self.lenght_box_z] + self.position_vertex_0 = origin_array + self.position_vertex_1 = origin_array + np.array([dim[0], .0, .0]) + self.position_vertex_2 = origin_array + np.array([.0, dim[1], .0]) + self.position_vertex_3 = origin_array + np.array([.0, .0, dim[2]]) + + def _set_mapping(self): + dim = [self.lenght_box_x, self.lenght_box_y, self.lenght_box_z] + self.psi_mapping = np.diag([1. / dim[i] for i in range(3)]) + self.inv_psi_mapping = np.diag(dim) + + def _set_transformation_params_to_zero(self): + 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) + + def _calculate_bb_dimension(self, shape, tol=1e-6, triangualte=False, triangulate_tol=1e-1): + """ return the bounding box of the TopoDS_Shape `shape` + Parameters + ---------- + shape : TopoDS_Shape or a subclass such as TopoDS_Face + the shape to compute the bounding box from + tol: float + tolerance of the computed boundingbox + triangualte : bool + 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 + Returns + ------- + tuple: consisting of two tuples: first one has coords of minimum, the second one coords of maximum + """ + bbox = Bnd_Box() + bbox.SetGap(tol) + if triangualte: + BRepMesh_IncrementalMesh(shape, triangulate_tol) + brepbndlib_Add(shape, bbox, triangualte) + 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. - 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_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, 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 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, basestring): - 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.\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\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 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 + """ + 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_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, 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 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, basestring): + 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.\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\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 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/tests/test_ffdparams.py b/tests/test_ffdparams.py index 86be136..4ca337b 100644 --- a/tests/test_ffdparams.py +++ b/tests/test_ffdparams.py @@ -1,327 +1,405 @@ -from unittest import TestCase -import unittest -import pygem.params as ffdp -import numpy as np import filecmp import os +from unittest import TestCase + +import numpy as np +from OCC.BRepAlgoAPI import BRepAlgoAPI_Cut +from OCC.BRepPrimAPI import BRepPrimAPI_MakeSphere, BRepPrimAPI_MakeBox +from OCC.gp import gp_Pnt + +import pygem.params as ffdp class TestFFDParameters(TestCase): - def test_class_members_default_n_control_points(self): - params = ffdp.FFDParameters() - assert 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_x(self): - params = ffdp.FFDParameters() - assert params.lenght_box_x == 1. - - def test_class_members_default_lenght_box_y(self): - params = ffdp.FFDParameters() - assert params.lenght_box_y == 1. - - def test_class_members_default_lenght_box_z(self): - params = ffdp.FFDParameters() - assert params.lenght_box_z == 1. - - def test_class_members_default_origin_box(self): - params = ffdp.FFDParameters() - np.testing.assert_array_almost_equal(params.origin_box, np.zeros(3)) - - def test_class_members_default_rot_angle_x(self): - params = ffdp.FFDParameters() - assert params.rot_angle_x == 0 - - def test_class_members_default_rot_angle_y(self): - params = ffdp.FFDParameters() - assert params.rot_angle_y == 0 - - def test_class_members_default_rot_angle_z(self): - params = ffdp.FFDParameters() - assert params.rot_angle_z == 0 - - 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 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 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 params.lenght_box_x == 45.0 - - def test_read_parameters_lenght_box_y(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.lenght_box_y == 90.0 - - def test_read_parameters_lenght_box_z(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.lenght_box_z == 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 params.rot_angle_x == 20.3 - - def test_read_parameters_rot_angle_y(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.rot_angle_y == 11.0 - - def test_read_parameters_rot_angle_z(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.rot_angle_z == 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)) - 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]).reshape((3,3)) - 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_info(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.print_info() + def test_class_members_default_n_control_points(self): + params = ffdp.FFDParameters() + assert 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_x(self): + params = ffdp.FFDParameters() + assert params.lenght_box_x == 1. + + def test_class_members_default_lenght_box_y(self): + params = ffdp.FFDParameters() + assert params.lenght_box_y == 1. + + def test_class_members_default_lenght_box_z(self): + params = ffdp.FFDParameters() + assert params.lenght_box_z == 1. + + def test_class_members_default_origin_box(self): + params = ffdp.FFDParameters() + np.testing.assert_array_almost_equal(params.origin_box, np.zeros(3)) + + def test_class_members_default_rot_angle_x(self): + params = ffdp.FFDParameters() + assert params.rot_angle_x == 0 + + def test_class_members_default_rot_angle_y(self): + params = ffdp.FFDParameters() + assert params.rot_angle_y == 0 + + def test_class_members_default_rot_angle_z(self): + params = ffdp.FFDParameters() + assert params.rot_angle_z == 0 + + 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 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 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 params.lenght_box_x == 45.0 + + def test_read_parameters_lenght_box_y(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.lenght_box_y == 90.0 + + def test_read_parameters_lenght_box_z(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.lenght_box_z == 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 params.rot_angle_x == 20.3 + + def test_read_parameters_rot_angle_y(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.rot_angle_y == 11.0 + + def test_read_parameters_rot_angle_z(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.rot_angle_z == 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)) + 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 + ]).reshape((3, 3)) + 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_info(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.print_info() + + def test_set_box_origin(self): + origin = np.array([0., 0., 0.]) + + params = ffdp.FFDParameters() + params._set_box_origin(origin) + np.testing.assert_almost_equal(params.origin_box, origin) + + def test_set_box_dimension(self): + origin = np.array([0., 0., 0.]) + tops = np.array([10., 10., 10.]) + params = ffdp.FFDParameters() + params._set_box_origin(origin) + params._set_box_dimensions(origin, tops) + self.assertEqual(params.lenght_box_x, tops[0]) + self.assertEqual(params.lenght_box_y, tops[1]) + self.assertEqual(params.lenght_box_z, tops[2]) + + 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._set_box_origin(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._set_box_origin(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, triangualte=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) From fa63918c67079c37e5730791aaac2a86cf06452f Mon Sep 17 00:00:00 2001 From: Maciej Grochowicz Date: Wed, 19 Oct 2016 11:52:04 +0200 Subject: [PATCH 3/8] Convert intendation (spaces -> tabs) --- pygem/params.py | 984 ++++++++++++++++++++-------------------- tests/test_ffdparams.py | 784 ++++++++++++++++---------------- 2 files changed, 884 insertions(+), 884 deletions(-) diff --git a/pygem/params.py b/pygem/params.py index d2be80e..a49ba2e 100644 --- a/pygem/params.py +++ b/pygem/params.py @@ -13,504 +13,504 @@ class FFDParameters(object): - """ - Class that handles the Free Form Deformation parameters in terms of FFD bounding box and - weight of the FFD control points. + """ + Class that handles the Free Form Deformation parameters in terms of FFD bounding box and + weight of the FFD control points. - :param x_ctrl_pnts (int): Number of control points along OX axis - :param y_ctrl_pnts (int): Number of control points along OY axis - :param z_ctrl_pnts (int): Number of control points along OZ axis + :param x_ctrl_pnts (int): Number of control points along OX axis + :param y_ctrl_pnts (int): Number of control points along OY axis + :param z_ctrl_pnts (int): Number of control points along OZ axis - :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 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 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 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 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: - - >>> import pygem.params as ffdp - - >>> # Reading an existing file - >>> params1 = ffdp.FFDParameters() - >>> params1.read_parameters(filename='tests/test_datasets/parameters_test_ffd_identity.prm') - - .. 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_x = 1. - self.lenght_box_y = 1. - self.lenght_box_z = 1. - - self.origin_box = np.array([0., 0., 0.]) - - self.rot_angle_x = 0. - self.rot_angle_y = 0. - self.rot_angle_z = 0. - - if n_control_points is None: - self.n_control_points = [2, 2, 2] - else: - self.n_control_points = n_control_points - - self._set_transformation_params_to_zero() - - self.psi_mapping = np.diag([1. / self.lenght_box_x, 1. / self.lenght_box_y, 1. / self.lenght_box_z]) - self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) - - 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.]) - self.array_mu_x = np.zeros(shape=self.n_control_points) - self.array_mu_y = np.zeros(shape=self.n_control_points) - self.array_mu_z = np.zeros(shape=self.n_control_points) - - 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. - 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_x = config.getfloat('Box info', 'box lenght x') - self.lenght_box_y = config.getfloat('Box info', 'box lenght y') - self.lenght_box_z = config.getfloat('Box info', 'box lenght z') - - origin_box_x = config.getfloat('Box info', 'box origin x') - origin_box_y = config.getfloat('Box info', 'box origin y') - origin_box_z = config.getfloat('Box info', 'box origin z') - self.origin_box = np.array([origin_box_x, origin_box_y, origin_box_z]) - - self.rot_angle_x = config.getfloat('Box info', 'rotation angle x') - self.rot_angle_y = config.getfloat('Box info', 'rotation angle y') - self.rot_angle_z = config.getfloat('Box info', 'rotation angle z') - - self.array_mu_x = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) - mux = config.get('Parameters weights', 'parameter x') - lines = mux.split('\n') - for line in lines: - values = line.split() - self.array_mu_x[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - - self.array_mu_y = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) - muy = config.get('Parameters weights', 'parameter y') - lines = muy.split('\n') - for line in lines: - values = line.split() - self.array_mu_y[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - - self.array_mu_z = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) - muz = config.get('Parameters weights', 'parameter z') - lines = muz.split('\n') - for line in lines: - values = line.split() - self.array_mu_z[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - - self.rotation_matrix = at.angles2matrix( - self.rot_angle_z * np.pi / 180, - self.rot_angle_y * np.pi / 180, - self.rot_angle_x * np.pi / 180 - ) - - self.position_vertex_0 = self.origin_box - self.position_vertex_1 = self.position_vertex_0 + np.dot(self.rotation_matrix, [self.lenght_box_x, 0, 0]) - self.position_vertex_2 = self.position_vertex_0 + np.dot(self.rotation_matrix, [0, self.lenght_box_y, 0]) - self.position_vertex_3 = self.position_vertex_0 + np.dot(self.rotation_matrix, [0, 0, self.lenght_box_z]) - - self.psi_mapping = np.diag([1. / self.lenght_box_x, 1. / self.lenght_box_y, 1. / self.lenght_box_z]) - self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) - - 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, basestring): - raise TypeError("filename must be a string") - - with open(filename, 'w') as output_file: - output_file.write('\n[Box info]\n') - output_file.write('# This section collects all the properties of the FFD bounding box.\n') - - output_file.write( - '\n# n control points indicates the number of control points in each direction (x, y, z).\n') - output_file.write( - '# For example, to create a 2 x 3 x 2 grid, use the following: n control points: 2, 3, 2\n') - output_file.write('n control points x: ' + str(self.n_control_points[0]) + '\n') - output_file.write('n control points y: ' + str(self.n_control_points[1]) + '\n') - output_file.write('n control points z: ' + str(self.n_control_points[2]) + '\n') - - output_file.write( - '\n# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).\n') - output_file.write('# It uses the local coordinate system.\n') - output_file.write( - '# 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_file.write('box lenght x: ' + str(self.lenght_box_x) + '\n') - output_file.write('box lenght y: ' + str(self.lenght_box_y) + '\n') - output_file.write('box lenght z: ' + str(self.lenght_box_z) + '\n') - - output_file.write( - '\n# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of\n') - output_file.write( - '# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].\n') - output_file.write('# See section "Parameters weights" for more details.\n') - output_file.write( - '# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.\n') - output_file.write('box origin x: ' + str(self.origin_box[0]) + '\n') - output_file.write('box origin y: ' + str(self.origin_box[1]) + '\n') - output_file.write('box origin z: ' + str(self.origin_box[2]) + '\n') - - output_file.write( - '\n# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.\n') - output_file.write('# The rotation is done with respect to the box origin.\n') - output_file.write( - '# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.\n') - output_file.write('rotation angle x: ' + str(self.rot_angle_x) + '\n') - output_file.write('rotation angle y: ' + str(self.rot_angle_y) + '\n') - output_file.write('rotation angle z: ' + str(self.rot_angle_z) + '\n') - - output_file.write('\n\n[Parameters weights]\n') - output_file.write('# This section describes the weights of the FFD control points.\n') - output_file.write('# We adopt the following convention:\n') - output_file.write( - '# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.\n') - output_file.write('# If a weight is equal to zero you can discard the line since the default is zero.\n') - output_file.write('#\n') - output_file.write('# | x index | y index | z index | weight |\n') - output_file.write('# --------------------------------------\n') - output_file.write('# | 0 | 0 | 0 | 1.0 |\n') - output_file.write( - '# | 0 | 1 | 1 | 0.0 | --> you can erase this line without effects\n') - output_file.write('# | 0 | 1 | 0 | -2.1 |\n') - output_file.write('# | 0 | 0 | 1 | 3.4 |\n') - - output_file.write('\n# parameter x collects the displacements along x, normalized with the box lenght x.') - output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_x[i][j][k]) + '\n') - offset = 13 - - output_file.write('\n# parameter y collects the displacements along y, normalized with the box lenght y.') - output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_y[i][j][k]) + '\n') - offset = 13 - - output_file.write('\n# parameter z collects the displacements along z, normalized with the box lenght z.') - output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_z[i][j][k]) + '\n') - offset = 13 - - def print_info(self): - """ - This method prints all the FFD parameters on the screen. Its purpose is for debugging. - """ - 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) + ')' - 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) + ')' - print '\narray_mu_x =' - print self.array_mu_x - print '\narray_mu_y =' - print self.array_mu_y - print '\narray_mu_z =' - print self.array_mu_z - print '\npsi_mapping =' - print self.psi_mapping - print '\ninv_psi_mapping =' - print self.inv_psi_mapping - print '\nrotation_matrix =' - print self.rotation_matrix - print '\nposition_vertex_0 =' - print self.position_vertex_0 - print '\nposition_vertex_1 =' - print self.position_vertex_1 - print '\nposition_vertex_2 =' - print self.position_vertex_2 - print '\nposition_vertex_3 =' - print self.position_vertex_3 - - def build_bounding_box(self, shape, tol=1e-6, triangualte=False, triangulate_tol=1e-1): - min_xyz, max_xyz = self._calculate_bb_dimension(shape, tol, triangualte, 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_origin(self, xyz): - self.origin_box = xyz - - 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. - :return: - """ - dims = [max_xyz[i] - min_xyz[i] for i in range(3)] - self.lenght_box_x = dims[0] - self.lenght_box_y = dims[1] - self.lenght_box_z = dims[2] - - def _set_position_of_vertices(self): - """ - Vertices of the control box around the object are set in this method. - Four vertex (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. - :return: - """ - origin_array = np.array(self.origin_box) - dim = [self.lenght_box_x, self.lenght_box_y, self.lenght_box_z] - self.position_vertex_0 = origin_array - self.position_vertex_1 = origin_array + np.array([dim[0], .0, .0]) - self.position_vertex_2 = origin_array + np.array([.0, dim[1], .0]) - self.position_vertex_3 = origin_array + np.array([.0, .0, dim[2]]) - - def _set_mapping(self): - dim = [self.lenght_box_x, self.lenght_box_y, self.lenght_box_z] - self.psi_mapping = np.diag([1. / dim[i] for i in range(3)]) - self.inv_psi_mapping = np.diag(dim) - - def _set_transformation_params_to_zero(self): - 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) - - def _calculate_bb_dimension(self, shape, tol=1e-6, triangualte=False, triangulate_tol=1e-1): - """ return the bounding box of the TopoDS_Shape `shape` - Parameters - ---------- - shape : TopoDS_Shape or a subclass such as TopoDS_Face - the shape to compute the bounding box from - tol: float - tolerance of the computed boundingbox - triangualte : bool - 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 - Returns - ------- - tuple: consisting of two tuples: first one has coords of minimum, the second one coords of maximum - """ - bbox = Bnd_Box() - bbox.SetGap(tol) - if triangualte: - BRepMesh_IncrementalMesh(shape, triangulate_tol) - brepbndlib_Add(shape, bbox, triangualte) - 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 + :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: + + >>> import pygem.params as ffdp + + >>> # Reading an existing file + >>> params1 = ffdp.FFDParameters() + >>> params1.read_parameters(filename='tests/test_datasets/parameters_test_ffd_identity.prm') + + .. 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_x = 1. + self.lenght_box_y = 1. + self.lenght_box_z = 1. + + self.origin_box = np.array([0., 0., 0.]) + + self.rot_angle_x = 0. + self.rot_angle_y = 0. + self.rot_angle_z = 0. + + if n_control_points is None: + self.n_control_points = [2, 2, 2] + else: + self.n_control_points = n_control_points + + self._set_transformation_params_to_zero() + + self.psi_mapping = np.diag([1. / self.lenght_box_x, 1. / self.lenght_box_y, 1. / self.lenght_box_z]) + self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) + + 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.]) + self.array_mu_x = np.zeros(shape=self.n_control_points) + self.array_mu_y = np.zeros(shape=self.n_control_points) + self.array_mu_z = np.zeros(shape=self.n_control_points) + + 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. + 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_x = config.getfloat('Box info', 'box lenght x') + self.lenght_box_y = config.getfloat('Box info', 'box lenght y') + self.lenght_box_z = config.getfloat('Box info', 'box lenght z') + + origin_box_x = config.getfloat('Box info', 'box origin x') + origin_box_y = config.getfloat('Box info', 'box origin y') + origin_box_z = config.getfloat('Box info', 'box origin z') + self.origin_box = np.array([origin_box_x, origin_box_y, origin_box_z]) + + self.rot_angle_x = config.getfloat('Box info', 'rotation angle x') + self.rot_angle_y = config.getfloat('Box info', 'rotation angle y') + self.rot_angle_z = config.getfloat('Box info', 'rotation angle z') + + self.array_mu_x = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + mux = config.get('Parameters weights', 'parameter x') + lines = mux.split('\n') + for line in lines: + values = line.split() + self.array_mu_x[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) + + self.array_mu_y = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + muy = config.get('Parameters weights', 'parameter y') + lines = muy.split('\n') + for line in lines: + values = line.split() + self.array_mu_y[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) + + self.array_mu_z = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + muz = config.get('Parameters weights', 'parameter z') + lines = muz.split('\n') + for line in lines: + values = line.split() + self.array_mu_z[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) + + self.rotation_matrix = at.angles2matrix( + self.rot_angle_z * np.pi / 180, + self.rot_angle_y * np.pi / 180, + self.rot_angle_x * np.pi / 180 + ) + + self.position_vertex_0 = self.origin_box + self.position_vertex_1 = self.position_vertex_0 + np.dot(self.rotation_matrix, [self.lenght_box_x, 0, 0]) + self.position_vertex_2 = self.position_vertex_0 + np.dot(self.rotation_matrix, [0, self.lenght_box_y, 0]) + self.position_vertex_3 = self.position_vertex_0 + np.dot(self.rotation_matrix, [0, 0, self.lenght_box_z]) + + self.psi_mapping = np.diag([1. / self.lenght_box_x, 1. / self.lenght_box_y, 1. / self.lenght_box_z]) + self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) + + 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, basestring): + raise TypeError("filename must be a string") + + with open(filename, 'w') as output_file: + output_file.write('\n[Box info]\n') + output_file.write('# This section collects all the properties of the FFD bounding box.\n') + + output_file.write( + '\n# n control points indicates the number of control points in each direction (x, y, z).\n') + output_file.write( + '# For example, to create a 2 x 3 x 2 grid, use the following: n control points: 2, 3, 2\n') + output_file.write('n control points x: ' + str(self.n_control_points[0]) + '\n') + output_file.write('n control points y: ' + str(self.n_control_points[1]) + '\n') + output_file.write('n control points z: ' + str(self.n_control_points[2]) + '\n') + + output_file.write( + '\n# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).\n') + output_file.write('# It uses the local coordinate system.\n') + output_file.write( + '# 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_file.write('box lenght x: ' + str(self.lenght_box_x) + '\n') + output_file.write('box lenght y: ' + str(self.lenght_box_y) + '\n') + output_file.write('box lenght z: ' + str(self.lenght_box_z) + '\n') + + output_file.write( + '\n# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of\n') + output_file.write( + '# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].\n') + output_file.write('# See section "Parameters weights" for more details.\n') + output_file.write( + '# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.\n') + output_file.write('box origin x: ' + str(self.origin_box[0]) + '\n') + output_file.write('box origin y: ' + str(self.origin_box[1]) + '\n') + output_file.write('box origin z: ' + str(self.origin_box[2]) + '\n') + + output_file.write( + '\n# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.\n') + output_file.write('# The rotation is done with respect to the box origin.\n') + output_file.write( + '# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.\n') + output_file.write('rotation angle x: ' + str(self.rot_angle_x) + '\n') + output_file.write('rotation angle y: ' + str(self.rot_angle_y) + '\n') + output_file.write('rotation angle z: ' + str(self.rot_angle_z) + '\n') + + output_file.write('\n\n[Parameters weights]\n') + output_file.write('# This section describes the weights of the FFD control points.\n') + output_file.write('# We adopt the following convention:\n') + output_file.write( + '# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.\n') + output_file.write('# If a weight is equal to zero you can discard the line since the default is zero.\n') + output_file.write('#\n') + output_file.write('# | x index | y index | z index | weight |\n') + output_file.write('# --------------------------------------\n') + output_file.write('# | 0 | 0 | 0 | 1.0 |\n') + output_file.write( + '# | 0 | 1 | 1 | 0.0 | --> you can erase this line without effects\n') + output_file.write('# | 0 | 1 | 0 | -2.1 |\n') + output_file.write('# | 0 | 0 | 1 | 3.4 |\n') + + output_file.write('\n# parameter x collects the displacements along x, normalized with the box lenght x.') + output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ + ' ' + str(self.array_mu_x[i][j][k]) + '\n') + offset = 13 + + output_file.write('\n# parameter y collects the displacements along y, normalized with the box lenght y.') + output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ + ' ' + str(self.array_mu_y[i][j][k]) + '\n') + offset = 13 + + output_file.write('\n# parameter z collects the displacements along z, normalized with the box lenght z.') + output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ + ' ' + str(self.array_mu_z[i][j][k]) + '\n') + offset = 13 + + def print_info(self): + """ + This method prints all the FFD parameters on the screen. Its purpose is for debugging. + """ + 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) + ')' + 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) + ')' + print '\narray_mu_x =' + print self.array_mu_x + print '\narray_mu_y =' + print self.array_mu_y + print '\narray_mu_z =' + print self.array_mu_z + print '\npsi_mapping =' + print self.psi_mapping + print '\ninv_psi_mapping =' + print self.inv_psi_mapping + print '\nrotation_matrix =' + print self.rotation_matrix + print '\nposition_vertex_0 =' + print self.position_vertex_0 + print '\nposition_vertex_1 =' + print self.position_vertex_1 + print '\nposition_vertex_2 =' + print self.position_vertex_2 + print '\nposition_vertex_3 =' + print self.position_vertex_3 + + def build_bounding_box(self, shape, tol=1e-6, triangualte=False, triangulate_tol=1e-1): + min_xyz, max_xyz = self._calculate_bb_dimension(shape, tol, triangualte, 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_origin(self, xyz): + self.origin_box = xyz + + 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. + :return: + """ + dims = [max_xyz[i] - min_xyz[i] for i in range(3)] + self.lenght_box_x = dims[0] + self.lenght_box_y = dims[1] + self.lenght_box_z = dims[2] + + def _set_position_of_vertices(self): + """ + Vertices of the control box around the object are set in this method. + Four vertex (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. + :return: + """ + origin_array = np.array(self.origin_box) + dim = [self.lenght_box_x, self.lenght_box_y, self.lenght_box_z] + self.position_vertex_0 = origin_array + self.position_vertex_1 = origin_array + np.array([dim[0], .0, .0]) + self.position_vertex_2 = origin_array + np.array([.0, dim[1], .0]) + self.position_vertex_3 = origin_array + np.array([.0, .0, dim[2]]) + + def _set_mapping(self): + dim = [self.lenght_box_x, self.lenght_box_y, self.lenght_box_z] + self.psi_mapping = np.diag([1. / dim[i] for i in range(3)]) + self.inv_psi_mapping = np.diag(dim) + + def _set_transformation_params_to_zero(self): + 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) + + def _calculate_bb_dimension(self, shape, tol=1e-6, triangualte=False, triangulate_tol=1e-1): + """ return the bounding box of the TopoDS_Shape `shape` + Parameters + ---------- + shape : TopoDS_Shape or a subclass such as TopoDS_Face + the shape to compute the bounding box from + tol: float + tolerance of the computed boundingbox + triangualte : bool + 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 + Returns + ------- + tuple: consisting of two tuples: first one has coords of minimum, the second one coords of maximum + """ + bbox = Bnd_Box() + bbox.SetGap(tol) + if triangualte: + BRepMesh_IncrementalMesh(shape, triangulate_tol) + brepbndlib_Add(shape, bbox, triangualte) + 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. - 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_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, 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 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, basestring): - 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.\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\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 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 + """ + 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_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, 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 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, basestring): + 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.\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\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 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/tests/test_ffdparams.py b/tests/test_ffdparams.py index 4ca337b..f594e05 100644 --- a/tests/test_ffdparams.py +++ b/tests/test_ffdparams.py @@ -11,395 +11,395 @@ class TestFFDParameters(TestCase): - def test_class_members_default_n_control_points(self): - params = ffdp.FFDParameters() - assert 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_x(self): - params = ffdp.FFDParameters() - assert params.lenght_box_x == 1. - - def test_class_members_default_lenght_box_y(self): - params = ffdp.FFDParameters() - assert params.lenght_box_y == 1. - - def test_class_members_default_lenght_box_z(self): - params = ffdp.FFDParameters() - assert params.lenght_box_z == 1. - - def test_class_members_default_origin_box(self): - params = ffdp.FFDParameters() - np.testing.assert_array_almost_equal(params.origin_box, np.zeros(3)) - - def test_class_members_default_rot_angle_x(self): - params = ffdp.FFDParameters() - assert params.rot_angle_x == 0 - - def test_class_members_default_rot_angle_y(self): - params = ffdp.FFDParameters() - assert params.rot_angle_y == 0 - - def test_class_members_default_rot_angle_z(self): - params = ffdp.FFDParameters() - assert params.rot_angle_z == 0 - - 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 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 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 params.lenght_box_x == 45.0 - - def test_read_parameters_lenght_box_y(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.lenght_box_y == 90.0 - - def test_read_parameters_lenght_box_z(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.lenght_box_z == 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 params.rot_angle_x == 20.3 - - def test_read_parameters_rot_angle_y(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.rot_angle_y == 11.0 - - def test_read_parameters_rot_angle_z(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.rot_angle_z == 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)) - 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 - ]).reshape((3, 3)) - 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_info(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.print_info() - - def test_set_box_origin(self): - origin = np.array([0., 0., 0.]) - - params = ffdp.FFDParameters() - params._set_box_origin(origin) - np.testing.assert_almost_equal(params.origin_box, origin) - - def test_set_box_dimension(self): - origin = np.array([0., 0., 0.]) - tops = np.array([10., 10., 10.]) - params = ffdp.FFDParameters() - params._set_box_origin(origin) - params._set_box_dimensions(origin, tops) - self.assertEqual(params.lenght_box_x, tops[0]) - self.assertEqual(params.lenght_box_y, tops[1]) - self.assertEqual(params.lenght_box_z, tops[2]) - - 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._set_box_origin(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._set_box_origin(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, triangualte=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 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_x(self): + params = ffdp.FFDParameters() + assert params.lenght_box_x == 1. + + def test_class_members_default_lenght_box_y(self): + params = ffdp.FFDParameters() + assert params.lenght_box_y == 1. + + def test_class_members_default_lenght_box_z(self): + params = ffdp.FFDParameters() + assert params.lenght_box_z == 1. + + def test_class_members_default_origin_box(self): + params = ffdp.FFDParameters() + np.testing.assert_array_almost_equal(params.origin_box, np.zeros(3)) + + def test_class_members_default_rot_angle_x(self): + params = ffdp.FFDParameters() + assert params.rot_angle_x == 0 + + def test_class_members_default_rot_angle_y(self): + params = ffdp.FFDParameters() + assert params.rot_angle_y == 0 + + def test_class_members_default_rot_angle_z(self): + params = ffdp.FFDParameters() + assert params.rot_angle_z == 0 + + 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 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 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 params.lenght_box_x == 45.0 + + def test_read_parameters_lenght_box_y(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.lenght_box_y == 90.0 + + def test_read_parameters_lenght_box_z(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.lenght_box_z == 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 params.rot_angle_x == 20.3 + + def test_read_parameters_rot_angle_y(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.rot_angle_y == 11.0 + + def test_read_parameters_rot_angle_z(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.rot_angle_z == 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)) + 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 + ]).reshape((3, 3)) + 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_info(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.print_info() + + def test_set_box_origin(self): + origin = np.array([0., 0., 0.]) + + params = ffdp.FFDParameters() + params._set_box_origin(origin) + np.testing.assert_almost_equal(params.origin_box, origin) + + def test_set_box_dimension(self): + origin = np.array([0., 0., 0.]) + tops = np.array([10., 10., 10.]) + params = ffdp.FFDParameters() + params._set_box_origin(origin) + params._set_box_dimensions(origin, tops) + self.assertEqual(params.lenght_box_x, tops[0]) + self.assertEqual(params.lenght_box_y, tops[1]) + self.assertEqual(params.lenght_box_z, tops[2]) + + 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._set_box_origin(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._set_box_origin(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, triangualte=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) From c1bedd32362e6f1d2456d4983b64a2190f21743c Mon Sep 17 00:00:00 2001 From: Maciej Grochowicz Date: Mon, 17 Oct 2016 13:20:45 +0200 Subject: [PATCH 4/8] Add bounding box params --- pygem/params.py | 922 ++++++++++++++++++++++------------------ tests/test_ffdparams.py | 722 +++++++++++++++++-------------- 2 files changed, 900 insertions(+), 744 deletions(-) diff --git a/pygem/params.py b/pygem/params.py index f237de2..d2be80e 100644 --- a/pygem/params.py +++ b/pygem/params.py @@ -1,438 +1,516 @@ """ Utilities for reading and writing parameters files to perform the desired geometrical morphing. """ -import os import ConfigParser +import os + import numpy as np +from OCC.BRepBndLib import brepbndlib_Add +from OCC.BRepMesh import BRepMesh_IncrementalMesh +from OCC.Bnd import Bnd_Box + import pygem.affine as at 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: - - >>> 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 defaul paramters 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') - - .. 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. + """ + Class that handles the Free Form Deformation parameters in terms of FFD bounding box and + weight of the FFD control points. + + :param x_ctrl_pnts (int): Number of control points along OX axis + :param y_ctrl_pnts (int): Number of control points along OY axis + :param z_ctrl_pnts (int): Number of control points along OZ axis + + :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: + + >>> import pygem.params as ffdp + + >>> # Reading an existing file + >>> params1 = ffdp.FFDParameters() + >>> params1.read_parameters(filename='tests/test_datasets/parameters_test_ffd_identity.prm') + + .. 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_x = 1. - self.lenght_box_y = 1. - self.lenght_box_z = 1. - - self.origin_box = np.array([0., 0., 0.]) - - self.rot_angle_x = 0. - self.rot_angle_y = 0. - self.rot_angle_z = 0. - - if n_control_points is None: - n_control_points = [2, 2, 2] - self.n_control_points = n_control_points - - self.array_mu_x = np.zeros((self.n_control_points[0], self.n_control_points[1], \ - self.n_control_points[2])) - self.array_mu_y = np.zeros((self.n_control_points[0], self.n_control_points[1], \ - self.n_control_points[2])) - self.array_mu_z = np.zeros((self.n_control_points[0], self.n_control_points[1], \ - self.n_control_points[2])) - - self.psi_mapping = np.diag([1./self.lenght_box_x, 1./self.lenght_box_y, 1./self.lenght_box_z]) - self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) - - 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, basestring): - 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_x = config.getfloat('Box info', 'box lenght x') - self.lenght_box_y = config.getfloat('Box info', 'box lenght y') - self.lenght_box_z = config.getfloat('Box info', 'box lenght z') - - origin_box_x = config.getfloat('Box info', 'box origin x') - origin_box_y = config.getfloat('Box info', 'box origin y') - origin_box_z = config.getfloat('Box info', 'box origin z') - self.origin_box = np.array([origin_box_x, origin_box_y, origin_box_z]) - - self.rot_angle_x = config.getfloat('Box info', 'rotation angle x') - self.rot_angle_y = config.getfloat('Box info', 'rotation angle y') - self.rot_angle_z = config.getfloat('Box info', 'rotation angle z') - - self.array_mu_x = np.zeros((self.n_control_points[0], self.n_control_points[1], \ - self.n_control_points[2])) - mux = config.get('Parameters weights', 'parameter x') - lines = mux.split('\n') - for line in lines: - values = line.split() - self.array_mu_x[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - - self.array_mu_y = np.zeros((self.n_control_points[0], self.n_control_points[1], \ - self.n_control_points[2])) - muy = config.get('Parameters weights', 'parameter y') - lines = muy.split('\n') - for line in lines: - values = line.split() - self.array_mu_y[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - - self.array_mu_z = np.zeros((self.n_control_points[0], self.n_control_points[1], \ - self.n_control_points[2])) - muz = config.get('Parameters weights', 'parameter z') - lines = muz.split('\n') - for line in lines: - values = line.split() - self.array_mu_z[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - - self.rotation_matrix = at.angles2matrix(self.rot_angle_z * np.pi/180, \ - self.rot_angle_y * np.pi/180, self.rot_angle_x * np.pi/180) - - self.position_vertex_0 = self.origin_box - self.position_vertex_1 = self.position_vertex_0 + \ - np.dot(self.rotation_matrix, [self.lenght_box_x, 0, 0]) - self.position_vertex_2 = self.position_vertex_0 + \ - np.dot(self.rotation_matrix, [0, self.lenght_box_y, 0]) - self.position_vertex_3 = self.position_vertex_0 + \ - np.dot(self.rotation_matrix, [0, 0, self.lenght_box_z]) - - self.psi_mapping = np.diag([1./self.lenght_box_x, 1./self.lenght_box_y, 1./self.lenght_box_z]) - self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) - - - 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, basestring): - raise TypeError("filename must be a string") - - with open(filename, 'w') as output_file: - output_file.write('\n[Box info]\n') - output_file.write('# This section collects all the properties of the FFD bounding box.\n') - - output_file.write('\n# n control points indicates the number of control points in each direction (x, y, z).\n') - output_file.write('# For example, to create a 2 x 3 x 2 grid, use the following: n control points: 2, 3, 2\n') - output_file.write('n control points x: ' + str(self.n_control_points[0]) + '\n') - output_file.write('n control points y: ' + str(self.n_control_points[1]) + '\n') - output_file.write('n control points z: ' + str(self.n_control_points[2]) + '\n') - - output_file.write('\n# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).\n') - output_file.write('# It uses the local coordinate system.\n') - output_file.write('# 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_file.write('box lenght x: ' + str(self.lenght_box_x) + '\n') - output_file.write('box lenght y: ' + str(self.lenght_box_y) + '\n') - output_file.write('box lenght z: ' + str(self.lenght_box_z) + '\n') - - output_file.write('\n# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of\n') - output_file.write('# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].\n') - output_file.write('# See section "Parameters weights" for more details.\n') - output_file.write('# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.\n') - output_file.write('box origin x: ' + str(self.origin_box[0]) + '\n') - output_file.write('box origin y: ' + str(self.origin_box[1]) + '\n') - output_file.write('box origin z: ' + str(self.origin_box[2]) + '\n') - - output_file.write('\n# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.\n') - output_file.write('# The rotation is done with respect to the box origin.\n') - output_file.write('# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.\n') - output_file.write('rotation angle x: ' + str(self.rot_angle_x) + '\n') - output_file.write('rotation angle y: ' + str(self.rot_angle_y) + '\n') - output_file.write('rotation angle z: ' + str(self.rot_angle_z) + '\n') - - output_file.write('\n\n[Parameters weights]\n') - output_file.write('# This section describes the weights of the FFD control points.\n') - output_file.write('# We adopt the following convention:\n') - output_file.write('# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.\n') - output_file.write('# If a weight is equal to zero you can discard the line since the default is zero.\n') - output_file.write('#\n') - output_file.write('# | x index | y index | z index | weight |\n') - output_file.write('# --------------------------------------\n') - output_file.write('# | 0 | 0 | 0 | 1.0 |\n') - output_file.write('# | 0 | 1 | 1 | 0.0 | --> you can erase this line without effects\n') - output_file.write('# | 0 | 1 | 0 | -2.1 |\n') - output_file.write('# | 0 | 0 | 1 | 3.4 |\n') - - output_file.write('\n# parameter x collects the displacements along x, normalized with the box lenght x.') - output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_x[i][j][k]) + '\n') - offset = 13 - - output_file.write('\n# parameter y collects the displacements along y, normalized with the box lenght y.') - output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_y[i][j][k]) + '\n') - offset = 13 - - output_file.write('\n# parameter z collects the displacements along z, normalized with the box lenght z.') - output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_z[i][j][k]) + '\n') - offset = 13 - - - def print_info(self): - """ - This method prints all the FFD parameters on the screen. Its purpose is for debugging. - """ - 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) + ')' - 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) + ')' - print '\narray_mu_x =' - print self.array_mu_x - print '\narray_mu_y =' - print self.array_mu_y - print '\narray_mu_z =' - print self.array_mu_z - print '\npsi_mapping =' - print self.psi_mapping - print '\ninv_psi_mapping =' - print self.inv_psi_mapping - print '\nrotation_matrix =' - print self.rotation_matrix - print '\nposition_vertex_0 =' - print self.position_vertex_0 - print '\nposition_vertex_1 =' - print self.position_vertex_1 - print '\nposition_vertex_2 =' - print self.position_vertex_2 - print '\nposition_vertex_3 =' - print self.position_vertex_3 + def __init__(self, n_control_points=None): + self.conversion_unit = 1. + + self.lenght_box_x = 1. + self.lenght_box_y = 1. + self.lenght_box_z = 1. + + self.origin_box = np.array([0., 0., 0.]) + + self.rot_angle_x = 0. + self.rot_angle_y = 0. + self.rot_angle_z = 0. + + if n_control_points is None: + self.n_control_points = [2, 2, 2] + else: + self.n_control_points = n_control_points + + self._set_transformation_params_to_zero() + + self.psi_mapping = np.diag([1. / self.lenght_box_x, 1. / self.lenght_box_y, 1. / self.lenght_box_z]) + self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) + + 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.]) + self.array_mu_x = np.zeros(shape=self.n_control_points) + self.array_mu_y = np.zeros(shape=self.n_control_points) + self.array_mu_z = np.zeros(shape=self.n_control_points) + + 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. + 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_x = config.getfloat('Box info', 'box lenght x') + self.lenght_box_y = config.getfloat('Box info', 'box lenght y') + self.lenght_box_z = config.getfloat('Box info', 'box lenght z') + + origin_box_x = config.getfloat('Box info', 'box origin x') + origin_box_y = config.getfloat('Box info', 'box origin y') + origin_box_z = config.getfloat('Box info', 'box origin z') + self.origin_box = np.array([origin_box_x, origin_box_y, origin_box_z]) + + self.rot_angle_x = config.getfloat('Box info', 'rotation angle x') + self.rot_angle_y = config.getfloat('Box info', 'rotation angle y') + self.rot_angle_z = config.getfloat('Box info', 'rotation angle z') + + self.array_mu_x = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + mux = config.get('Parameters weights', 'parameter x') + lines = mux.split('\n') + for line in lines: + values = line.split() + self.array_mu_x[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) + + self.array_mu_y = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + muy = config.get('Parameters weights', 'parameter y') + lines = muy.split('\n') + for line in lines: + values = line.split() + self.array_mu_y[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) + + self.array_mu_z = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + muz = config.get('Parameters weights', 'parameter z') + lines = muz.split('\n') + for line in lines: + values = line.split() + self.array_mu_z[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) + + self.rotation_matrix = at.angles2matrix( + self.rot_angle_z * np.pi / 180, + self.rot_angle_y * np.pi / 180, + self.rot_angle_x * np.pi / 180 + ) + + self.position_vertex_0 = self.origin_box + self.position_vertex_1 = self.position_vertex_0 + np.dot(self.rotation_matrix, [self.lenght_box_x, 0, 0]) + self.position_vertex_2 = self.position_vertex_0 + np.dot(self.rotation_matrix, [0, self.lenght_box_y, 0]) + self.position_vertex_3 = self.position_vertex_0 + np.dot(self.rotation_matrix, [0, 0, self.lenght_box_z]) + + self.psi_mapping = np.diag([1. / self.lenght_box_x, 1. / self.lenght_box_y, 1. / self.lenght_box_z]) + self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) + + 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, basestring): + raise TypeError("filename must be a string") + + with open(filename, 'w') as output_file: + output_file.write('\n[Box info]\n') + output_file.write('# This section collects all the properties of the FFD bounding box.\n') + + output_file.write( + '\n# n control points indicates the number of control points in each direction (x, y, z).\n') + output_file.write( + '# For example, to create a 2 x 3 x 2 grid, use the following: n control points: 2, 3, 2\n') + output_file.write('n control points x: ' + str(self.n_control_points[0]) + '\n') + output_file.write('n control points y: ' + str(self.n_control_points[1]) + '\n') + output_file.write('n control points z: ' + str(self.n_control_points[2]) + '\n') + + output_file.write( + '\n# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).\n') + output_file.write('# It uses the local coordinate system.\n') + output_file.write( + '# 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_file.write('box lenght x: ' + str(self.lenght_box_x) + '\n') + output_file.write('box lenght y: ' + str(self.lenght_box_y) + '\n') + output_file.write('box lenght z: ' + str(self.lenght_box_z) + '\n') + + output_file.write( + '\n# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of\n') + output_file.write( + '# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].\n') + output_file.write('# See section "Parameters weights" for more details.\n') + output_file.write( + '# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.\n') + output_file.write('box origin x: ' + str(self.origin_box[0]) + '\n') + output_file.write('box origin y: ' + str(self.origin_box[1]) + '\n') + output_file.write('box origin z: ' + str(self.origin_box[2]) + '\n') + + output_file.write( + '\n# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.\n') + output_file.write('# The rotation is done with respect to the box origin.\n') + output_file.write( + '# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.\n') + output_file.write('rotation angle x: ' + str(self.rot_angle_x) + '\n') + output_file.write('rotation angle y: ' + str(self.rot_angle_y) + '\n') + output_file.write('rotation angle z: ' + str(self.rot_angle_z) + '\n') + + output_file.write('\n\n[Parameters weights]\n') + output_file.write('# This section describes the weights of the FFD control points.\n') + output_file.write('# We adopt the following convention:\n') + output_file.write( + '# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.\n') + output_file.write('# If a weight is equal to zero you can discard the line since the default is zero.\n') + output_file.write('#\n') + output_file.write('# | x index | y index | z index | weight |\n') + output_file.write('# --------------------------------------\n') + output_file.write('# | 0 | 0 | 0 | 1.0 |\n') + output_file.write( + '# | 0 | 1 | 1 | 0.0 | --> you can erase this line without effects\n') + output_file.write('# | 0 | 1 | 0 | -2.1 |\n') + output_file.write('# | 0 | 0 | 1 | 3.4 |\n') + + output_file.write('\n# parameter x collects the displacements along x, normalized with the box lenght x.') + output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ + ' ' + str(self.array_mu_x[i][j][k]) + '\n') + offset = 13 + + output_file.write('\n# parameter y collects the displacements along y, normalized with the box lenght y.') + output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ + ' ' + str(self.array_mu_y[i][j][k]) + '\n') + offset = 13 + + output_file.write('\n# parameter z collects the displacements along z, normalized with the box lenght z.') + output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ + ' ' + str(self.array_mu_z[i][j][k]) + '\n') + offset = 13 + + def print_info(self): + """ + This method prints all the FFD parameters on the screen. Its purpose is for debugging. + """ + 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) + ')' + 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) + ')' + print '\narray_mu_x =' + print self.array_mu_x + print '\narray_mu_y =' + print self.array_mu_y + print '\narray_mu_z =' + print self.array_mu_z + print '\npsi_mapping =' + print self.psi_mapping + print '\ninv_psi_mapping =' + print self.inv_psi_mapping + print '\nrotation_matrix =' + print self.rotation_matrix + print '\nposition_vertex_0 =' + print self.position_vertex_0 + print '\nposition_vertex_1 =' + print self.position_vertex_1 + print '\nposition_vertex_2 =' + print self.position_vertex_2 + print '\nposition_vertex_3 =' + print self.position_vertex_3 + + def build_bounding_box(self, shape, tol=1e-6, triangualte=False, triangulate_tol=1e-1): + min_xyz, max_xyz = self._calculate_bb_dimension(shape, tol, triangualte, 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_origin(self, xyz): + self.origin_box = xyz + + 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. + :return: + """ + dims = [max_xyz[i] - min_xyz[i] for i in range(3)] + self.lenght_box_x = dims[0] + self.lenght_box_y = dims[1] + self.lenght_box_z = dims[2] + + def _set_position_of_vertices(self): + """ + Vertices of the control box around the object are set in this method. + Four vertex (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. + :return: + """ + origin_array = np.array(self.origin_box) + dim = [self.lenght_box_x, self.lenght_box_y, self.lenght_box_z] + self.position_vertex_0 = origin_array + self.position_vertex_1 = origin_array + np.array([dim[0], .0, .0]) + self.position_vertex_2 = origin_array + np.array([.0, dim[1], .0]) + self.position_vertex_3 = origin_array + np.array([.0, .0, dim[2]]) + + def _set_mapping(self): + dim = [self.lenght_box_x, self.lenght_box_y, self.lenght_box_z] + self.psi_mapping = np.diag([1. / dim[i] for i in range(3)]) + self.inv_psi_mapping = np.diag(dim) + + def _set_transformation_params_to_zero(self): + 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) + + def _calculate_bb_dimension(self, shape, tol=1e-6, triangualte=False, triangulate_tol=1e-1): + """ return the bounding box of the TopoDS_Shape `shape` + Parameters + ---------- + shape : TopoDS_Shape or a subclass such as TopoDS_Face + the shape to compute the bounding box from + tol: float + tolerance of the computed boundingbox + triangualte : bool + 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 + Returns + ------- + tuple: consisting of two tuples: first one has coords of minimum, the second one coords of maximum + """ + bbox = Bnd_Box() + bbox.SetGap(tol) + if triangualte: + BRepMesh_IncrementalMesh(shape, triangulate_tol) + brepbndlib_Add(shape, bbox, triangualte) + 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. - 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_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, 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 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, basestring): - 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.\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\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 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 + """ + 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_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, 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 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, basestring): + 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.\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\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 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/tests/test_ffdparams.py b/tests/test_ffdparams.py index 86be136..4ca337b 100644 --- a/tests/test_ffdparams.py +++ b/tests/test_ffdparams.py @@ -1,327 +1,405 @@ -from unittest import TestCase -import unittest -import pygem.params as ffdp -import numpy as np import filecmp import os +from unittest import TestCase + +import numpy as np +from OCC.BRepAlgoAPI import BRepAlgoAPI_Cut +from OCC.BRepPrimAPI import BRepPrimAPI_MakeSphere, BRepPrimAPI_MakeBox +from OCC.gp import gp_Pnt + +import pygem.params as ffdp class TestFFDParameters(TestCase): - def test_class_members_default_n_control_points(self): - params = ffdp.FFDParameters() - assert 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_x(self): - params = ffdp.FFDParameters() - assert params.lenght_box_x == 1. - - def test_class_members_default_lenght_box_y(self): - params = ffdp.FFDParameters() - assert params.lenght_box_y == 1. - - def test_class_members_default_lenght_box_z(self): - params = ffdp.FFDParameters() - assert params.lenght_box_z == 1. - - def test_class_members_default_origin_box(self): - params = ffdp.FFDParameters() - np.testing.assert_array_almost_equal(params.origin_box, np.zeros(3)) - - def test_class_members_default_rot_angle_x(self): - params = ffdp.FFDParameters() - assert params.rot_angle_x == 0 - - def test_class_members_default_rot_angle_y(self): - params = ffdp.FFDParameters() - assert params.rot_angle_y == 0 - - def test_class_members_default_rot_angle_z(self): - params = ffdp.FFDParameters() - assert params.rot_angle_z == 0 - - 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 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 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 params.lenght_box_x == 45.0 - - def test_read_parameters_lenght_box_y(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.lenght_box_y == 90.0 - - def test_read_parameters_lenght_box_z(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.lenght_box_z == 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 params.rot_angle_x == 20.3 - - def test_read_parameters_rot_angle_y(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.rot_angle_y == 11.0 - - def test_read_parameters_rot_angle_z(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.rot_angle_z == 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)) - 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]).reshape((3,3)) - 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_info(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.print_info() + def test_class_members_default_n_control_points(self): + params = ffdp.FFDParameters() + assert 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_x(self): + params = ffdp.FFDParameters() + assert params.lenght_box_x == 1. + + def test_class_members_default_lenght_box_y(self): + params = ffdp.FFDParameters() + assert params.lenght_box_y == 1. + + def test_class_members_default_lenght_box_z(self): + params = ffdp.FFDParameters() + assert params.lenght_box_z == 1. + + def test_class_members_default_origin_box(self): + params = ffdp.FFDParameters() + np.testing.assert_array_almost_equal(params.origin_box, np.zeros(3)) + + def test_class_members_default_rot_angle_x(self): + params = ffdp.FFDParameters() + assert params.rot_angle_x == 0 + + def test_class_members_default_rot_angle_y(self): + params = ffdp.FFDParameters() + assert params.rot_angle_y == 0 + + def test_class_members_default_rot_angle_z(self): + params = ffdp.FFDParameters() + assert params.rot_angle_z == 0 + + 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 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 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 params.lenght_box_x == 45.0 + + def test_read_parameters_lenght_box_y(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.lenght_box_y == 90.0 + + def test_read_parameters_lenght_box_z(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.lenght_box_z == 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 params.rot_angle_x == 20.3 + + def test_read_parameters_rot_angle_y(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.rot_angle_y == 11.0 + + def test_read_parameters_rot_angle_z(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.rot_angle_z == 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)) + 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 + ]).reshape((3, 3)) + 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_info(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.print_info() + + def test_set_box_origin(self): + origin = np.array([0., 0., 0.]) + + params = ffdp.FFDParameters() + params._set_box_origin(origin) + np.testing.assert_almost_equal(params.origin_box, origin) + + def test_set_box_dimension(self): + origin = np.array([0., 0., 0.]) + tops = np.array([10., 10., 10.]) + params = ffdp.FFDParameters() + params._set_box_origin(origin) + params._set_box_dimensions(origin, tops) + self.assertEqual(params.lenght_box_x, tops[0]) + self.assertEqual(params.lenght_box_y, tops[1]) + self.assertEqual(params.lenght_box_z, tops[2]) + + 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._set_box_origin(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._set_box_origin(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, triangualte=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) From e9cd7eba38a5b51bcd1d4237d26581c0e654909e Mon Sep 17 00:00:00 2001 From: Maciej Grochowicz Date: Wed, 19 Oct 2016 11:52:04 +0200 Subject: [PATCH 5/8] Convert intendation (spaces -> tabs) --- pygem/params.py | 984 ++++++++++++++++++++-------------------- tests/test_ffdparams.py | 784 ++++++++++++++++---------------- 2 files changed, 884 insertions(+), 884 deletions(-) diff --git a/pygem/params.py b/pygem/params.py index d2be80e..a49ba2e 100644 --- a/pygem/params.py +++ b/pygem/params.py @@ -13,504 +13,504 @@ class FFDParameters(object): - """ - Class that handles the Free Form Deformation parameters in terms of FFD bounding box and - weight of the FFD control points. + """ + Class that handles the Free Form Deformation parameters in terms of FFD bounding box and + weight of the FFD control points. - :param x_ctrl_pnts (int): Number of control points along OX axis - :param y_ctrl_pnts (int): Number of control points along OY axis - :param z_ctrl_pnts (int): Number of control points along OZ axis + :param x_ctrl_pnts (int): Number of control points along OX axis + :param y_ctrl_pnts (int): Number of control points along OY axis + :param z_ctrl_pnts (int): Number of control points along OZ axis - :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 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 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 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 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: - - >>> import pygem.params as ffdp - - >>> # Reading an existing file - >>> params1 = ffdp.FFDParameters() - >>> params1.read_parameters(filename='tests/test_datasets/parameters_test_ffd_identity.prm') - - .. 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_x = 1. - self.lenght_box_y = 1. - self.lenght_box_z = 1. - - self.origin_box = np.array([0., 0., 0.]) - - self.rot_angle_x = 0. - self.rot_angle_y = 0. - self.rot_angle_z = 0. - - if n_control_points is None: - self.n_control_points = [2, 2, 2] - else: - self.n_control_points = n_control_points - - self._set_transformation_params_to_zero() - - self.psi_mapping = np.diag([1. / self.lenght_box_x, 1. / self.lenght_box_y, 1. / self.lenght_box_z]) - self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) - - 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.]) - self.array_mu_x = np.zeros(shape=self.n_control_points) - self.array_mu_y = np.zeros(shape=self.n_control_points) - self.array_mu_z = np.zeros(shape=self.n_control_points) - - 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. - 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_x = config.getfloat('Box info', 'box lenght x') - self.lenght_box_y = config.getfloat('Box info', 'box lenght y') - self.lenght_box_z = config.getfloat('Box info', 'box lenght z') - - origin_box_x = config.getfloat('Box info', 'box origin x') - origin_box_y = config.getfloat('Box info', 'box origin y') - origin_box_z = config.getfloat('Box info', 'box origin z') - self.origin_box = np.array([origin_box_x, origin_box_y, origin_box_z]) - - self.rot_angle_x = config.getfloat('Box info', 'rotation angle x') - self.rot_angle_y = config.getfloat('Box info', 'rotation angle y') - self.rot_angle_z = config.getfloat('Box info', 'rotation angle z') - - self.array_mu_x = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) - mux = config.get('Parameters weights', 'parameter x') - lines = mux.split('\n') - for line in lines: - values = line.split() - self.array_mu_x[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - - self.array_mu_y = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) - muy = config.get('Parameters weights', 'parameter y') - lines = muy.split('\n') - for line in lines: - values = line.split() - self.array_mu_y[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - - self.array_mu_z = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) - muz = config.get('Parameters weights', 'parameter z') - lines = muz.split('\n') - for line in lines: - values = line.split() - self.array_mu_z[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - - self.rotation_matrix = at.angles2matrix( - self.rot_angle_z * np.pi / 180, - self.rot_angle_y * np.pi / 180, - self.rot_angle_x * np.pi / 180 - ) - - self.position_vertex_0 = self.origin_box - self.position_vertex_1 = self.position_vertex_0 + np.dot(self.rotation_matrix, [self.lenght_box_x, 0, 0]) - self.position_vertex_2 = self.position_vertex_0 + np.dot(self.rotation_matrix, [0, self.lenght_box_y, 0]) - self.position_vertex_3 = self.position_vertex_0 + np.dot(self.rotation_matrix, [0, 0, self.lenght_box_z]) - - self.psi_mapping = np.diag([1. / self.lenght_box_x, 1. / self.lenght_box_y, 1. / self.lenght_box_z]) - self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) - - 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, basestring): - raise TypeError("filename must be a string") - - with open(filename, 'w') as output_file: - output_file.write('\n[Box info]\n') - output_file.write('# This section collects all the properties of the FFD bounding box.\n') - - output_file.write( - '\n# n control points indicates the number of control points in each direction (x, y, z).\n') - output_file.write( - '# For example, to create a 2 x 3 x 2 grid, use the following: n control points: 2, 3, 2\n') - output_file.write('n control points x: ' + str(self.n_control_points[0]) + '\n') - output_file.write('n control points y: ' + str(self.n_control_points[1]) + '\n') - output_file.write('n control points z: ' + str(self.n_control_points[2]) + '\n') - - output_file.write( - '\n# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).\n') - output_file.write('# It uses the local coordinate system.\n') - output_file.write( - '# 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_file.write('box lenght x: ' + str(self.lenght_box_x) + '\n') - output_file.write('box lenght y: ' + str(self.lenght_box_y) + '\n') - output_file.write('box lenght z: ' + str(self.lenght_box_z) + '\n') - - output_file.write( - '\n# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of\n') - output_file.write( - '# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].\n') - output_file.write('# See section "Parameters weights" for more details.\n') - output_file.write( - '# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.\n') - output_file.write('box origin x: ' + str(self.origin_box[0]) + '\n') - output_file.write('box origin y: ' + str(self.origin_box[1]) + '\n') - output_file.write('box origin z: ' + str(self.origin_box[2]) + '\n') - - output_file.write( - '\n# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.\n') - output_file.write('# The rotation is done with respect to the box origin.\n') - output_file.write( - '# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.\n') - output_file.write('rotation angle x: ' + str(self.rot_angle_x) + '\n') - output_file.write('rotation angle y: ' + str(self.rot_angle_y) + '\n') - output_file.write('rotation angle z: ' + str(self.rot_angle_z) + '\n') - - output_file.write('\n\n[Parameters weights]\n') - output_file.write('# This section describes the weights of the FFD control points.\n') - output_file.write('# We adopt the following convention:\n') - output_file.write( - '# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.\n') - output_file.write('# If a weight is equal to zero you can discard the line since the default is zero.\n') - output_file.write('#\n') - output_file.write('# | x index | y index | z index | weight |\n') - output_file.write('# --------------------------------------\n') - output_file.write('# | 0 | 0 | 0 | 1.0 |\n') - output_file.write( - '# | 0 | 1 | 1 | 0.0 | --> you can erase this line without effects\n') - output_file.write('# | 0 | 1 | 0 | -2.1 |\n') - output_file.write('# | 0 | 0 | 1 | 3.4 |\n') - - output_file.write('\n# parameter x collects the displacements along x, normalized with the box lenght x.') - output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_x[i][j][k]) + '\n') - offset = 13 - - output_file.write('\n# parameter y collects the displacements along y, normalized with the box lenght y.') - output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_y[i][j][k]) + '\n') - offset = 13 - - output_file.write('\n# parameter z collects the displacements along z, normalized with the box lenght z.') - output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_z[i][j][k]) + '\n') - offset = 13 - - def print_info(self): - """ - This method prints all the FFD parameters on the screen. Its purpose is for debugging. - """ - 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) + ')' - 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) + ')' - print '\narray_mu_x =' - print self.array_mu_x - print '\narray_mu_y =' - print self.array_mu_y - print '\narray_mu_z =' - print self.array_mu_z - print '\npsi_mapping =' - print self.psi_mapping - print '\ninv_psi_mapping =' - print self.inv_psi_mapping - print '\nrotation_matrix =' - print self.rotation_matrix - print '\nposition_vertex_0 =' - print self.position_vertex_0 - print '\nposition_vertex_1 =' - print self.position_vertex_1 - print '\nposition_vertex_2 =' - print self.position_vertex_2 - print '\nposition_vertex_3 =' - print self.position_vertex_3 - - def build_bounding_box(self, shape, tol=1e-6, triangualte=False, triangulate_tol=1e-1): - min_xyz, max_xyz = self._calculate_bb_dimension(shape, tol, triangualte, 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_origin(self, xyz): - self.origin_box = xyz - - 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. - :return: - """ - dims = [max_xyz[i] - min_xyz[i] for i in range(3)] - self.lenght_box_x = dims[0] - self.lenght_box_y = dims[1] - self.lenght_box_z = dims[2] - - def _set_position_of_vertices(self): - """ - Vertices of the control box around the object are set in this method. - Four vertex (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. - :return: - """ - origin_array = np.array(self.origin_box) - dim = [self.lenght_box_x, self.lenght_box_y, self.lenght_box_z] - self.position_vertex_0 = origin_array - self.position_vertex_1 = origin_array + np.array([dim[0], .0, .0]) - self.position_vertex_2 = origin_array + np.array([.0, dim[1], .0]) - self.position_vertex_3 = origin_array + np.array([.0, .0, dim[2]]) - - def _set_mapping(self): - dim = [self.lenght_box_x, self.lenght_box_y, self.lenght_box_z] - self.psi_mapping = np.diag([1. / dim[i] for i in range(3)]) - self.inv_psi_mapping = np.diag(dim) - - def _set_transformation_params_to_zero(self): - 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) - - def _calculate_bb_dimension(self, shape, tol=1e-6, triangualte=False, triangulate_tol=1e-1): - """ return the bounding box of the TopoDS_Shape `shape` - Parameters - ---------- - shape : TopoDS_Shape or a subclass such as TopoDS_Face - the shape to compute the bounding box from - tol: float - tolerance of the computed boundingbox - triangualte : bool - 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 - Returns - ------- - tuple: consisting of two tuples: first one has coords of minimum, the second one coords of maximum - """ - bbox = Bnd_Box() - bbox.SetGap(tol) - if triangualte: - BRepMesh_IncrementalMesh(shape, triangulate_tol) - brepbndlib_Add(shape, bbox, triangualte) - 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 + :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: + + >>> import pygem.params as ffdp + + >>> # Reading an existing file + >>> params1 = ffdp.FFDParameters() + >>> params1.read_parameters(filename='tests/test_datasets/parameters_test_ffd_identity.prm') + + .. 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_x = 1. + self.lenght_box_y = 1. + self.lenght_box_z = 1. + + self.origin_box = np.array([0., 0., 0.]) + + self.rot_angle_x = 0. + self.rot_angle_y = 0. + self.rot_angle_z = 0. + + if n_control_points is None: + self.n_control_points = [2, 2, 2] + else: + self.n_control_points = n_control_points + + self._set_transformation_params_to_zero() + + self.psi_mapping = np.diag([1. / self.lenght_box_x, 1. / self.lenght_box_y, 1. / self.lenght_box_z]) + self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) + + 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.]) + self.array_mu_x = np.zeros(shape=self.n_control_points) + self.array_mu_y = np.zeros(shape=self.n_control_points) + self.array_mu_z = np.zeros(shape=self.n_control_points) + + 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. + 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_x = config.getfloat('Box info', 'box lenght x') + self.lenght_box_y = config.getfloat('Box info', 'box lenght y') + self.lenght_box_z = config.getfloat('Box info', 'box lenght z') + + origin_box_x = config.getfloat('Box info', 'box origin x') + origin_box_y = config.getfloat('Box info', 'box origin y') + origin_box_z = config.getfloat('Box info', 'box origin z') + self.origin_box = np.array([origin_box_x, origin_box_y, origin_box_z]) + + self.rot_angle_x = config.getfloat('Box info', 'rotation angle x') + self.rot_angle_y = config.getfloat('Box info', 'rotation angle y') + self.rot_angle_z = config.getfloat('Box info', 'rotation angle z') + + self.array_mu_x = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + mux = config.get('Parameters weights', 'parameter x') + lines = mux.split('\n') + for line in lines: + values = line.split() + self.array_mu_x[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) + + self.array_mu_y = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + muy = config.get('Parameters weights', 'parameter y') + lines = muy.split('\n') + for line in lines: + values = line.split() + self.array_mu_y[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) + + self.array_mu_z = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + muz = config.get('Parameters weights', 'parameter z') + lines = muz.split('\n') + for line in lines: + values = line.split() + self.array_mu_z[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) + + self.rotation_matrix = at.angles2matrix( + self.rot_angle_z * np.pi / 180, + self.rot_angle_y * np.pi / 180, + self.rot_angle_x * np.pi / 180 + ) + + self.position_vertex_0 = self.origin_box + self.position_vertex_1 = self.position_vertex_0 + np.dot(self.rotation_matrix, [self.lenght_box_x, 0, 0]) + self.position_vertex_2 = self.position_vertex_0 + np.dot(self.rotation_matrix, [0, self.lenght_box_y, 0]) + self.position_vertex_3 = self.position_vertex_0 + np.dot(self.rotation_matrix, [0, 0, self.lenght_box_z]) + + self.psi_mapping = np.diag([1. / self.lenght_box_x, 1. / self.lenght_box_y, 1. / self.lenght_box_z]) + self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) + + 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, basestring): + raise TypeError("filename must be a string") + + with open(filename, 'w') as output_file: + output_file.write('\n[Box info]\n') + output_file.write('# This section collects all the properties of the FFD bounding box.\n') + + output_file.write( + '\n# n control points indicates the number of control points in each direction (x, y, z).\n') + output_file.write( + '# For example, to create a 2 x 3 x 2 grid, use the following: n control points: 2, 3, 2\n') + output_file.write('n control points x: ' + str(self.n_control_points[0]) + '\n') + output_file.write('n control points y: ' + str(self.n_control_points[1]) + '\n') + output_file.write('n control points z: ' + str(self.n_control_points[2]) + '\n') + + output_file.write( + '\n# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).\n') + output_file.write('# It uses the local coordinate system.\n') + output_file.write( + '# 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_file.write('box lenght x: ' + str(self.lenght_box_x) + '\n') + output_file.write('box lenght y: ' + str(self.lenght_box_y) + '\n') + output_file.write('box lenght z: ' + str(self.lenght_box_z) + '\n') + + output_file.write( + '\n# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of\n') + output_file.write( + '# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].\n') + output_file.write('# See section "Parameters weights" for more details.\n') + output_file.write( + '# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.\n') + output_file.write('box origin x: ' + str(self.origin_box[0]) + '\n') + output_file.write('box origin y: ' + str(self.origin_box[1]) + '\n') + output_file.write('box origin z: ' + str(self.origin_box[2]) + '\n') + + output_file.write( + '\n# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.\n') + output_file.write('# The rotation is done with respect to the box origin.\n') + output_file.write( + '# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.\n') + output_file.write('rotation angle x: ' + str(self.rot_angle_x) + '\n') + output_file.write('rotation angle y: ' + str(self.rot_angle_y) + '\n') + output_file.write('rotation angle z: ' + str(self.rot_angle_z) + '\n') + + output_file.write('\n\n[Parameters weights]\n') + output_file.write('# This section describes the weights of the FFD control points.\n') + output_file.write('# We adopt the following convention:\n') + output_file.write( + '# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.\n') + output_file.write('# If a weight is equal to zero you can discard the line since the default is zero.\n') + output_file.write('#\n') + output_file.write('# | x index | y index | z index | weight |\n') + output_file.write('# --------------------------------------\n') + output_file.write('# | 0 | 0 | 0 | 1.0 |\n') + output_file.write( + '# | 0 | 1 | 1 | 0.0 | --> you can erase this line without effects\n') + output_file.write('# | 0 | 1 | 0 | -2.1 |\n') + output_file.write('# | 0 | 0 | 1 | 3.4 |\n') + + output_file.write('\n# parameter x collects the displacements along x, normalized with the box lenght x.') + output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ + ' ' + str(self.array_mu_x[i][j][k]) + '\n') + offset = 13 + + output_file.write('\n# parameter y collects the displacements along y, normalized with the box lenght y.') + output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ + ' ' + str(self.array_mu_y[i][j][k]) + '\n') + offset = 13 + + output_file.write('\n# parameter z collects the displacements along z, normalized with the box lenght z.') + output_file.write('\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_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ + ' ' + str(self.array_mu_z[i][j][k]) + '\n') + offset = 13 + + def print_info(self): + """ + This method prints all the FFD parameters on the screen. Its purpose is for debugging. + """ + 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) + ')' + 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) + ')' + print '\narray_mu_x =' + print self.array_mu_x + print '\narray_mu_y =' + print self.array_mu_y + print '\narray_mu_z =' + print self.array_mu_z + print '\npsi_mapping =' + print self.psi_mapping + print '\ninv_psi_mapping =' + print self.inv_psi_mapping + print '\nrotation_matrix =' + print self.rotation_matrix + print '\nposition_vertex_0 =' + print self.position_vertex_0 + print '\nposition_vertex_1 =' + print self.position_vertex_1 + print '\nposition_vertex_2 =' + print self.position_vertex_2 + print '\nposition_vertex_3 =' + print self.position_vertex_3 + + def build_bounding_box(self, shape, tol=1e-6, triangualte=False, triangulate_tol=1e-1): + min_xyz, max_xyz = self._calculate_bb_dimension(shape, tol, triangualte, 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_origin(self, xyz): + self.origin_box = xyz + + 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. + :return: + """ + dims = [max_xyz[i] - min_xyz[i] for i in range(3)] + self.lenght_box_x = dims[0] + self.lenght_box_y = dims[1] + self.lenght_box_z = dims[2] + + def _set_position_of_vertices(self): + """ + Vertices of the control box around the object are set in this method. + Four vertex (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. + :return: + """ + origin_array = np.array(self.origin_box) + dim = [self.lenght_box_x, self.lenght_box_y, self.lenght_box_z] + self.position_vertex_0 = origin_array + self.position_vertex_1 = origin_array + np.array([dim[0], .0, .0]) + self.position_vertex_2 = origin_array + np.array([.0, dim[1], .0]) + self.position_vertex_3 = origin_array + np.array([.0, .0, dim[2]]) + + def _set_mapping(self): + dim = [self.lenght_box_x, self.lenght_box_y, self.lenght_box_z] + self.psi_mapping = np.diag([1. / dim[i] for i in range(3)]) + self.inv_psi_mapping = np.diag(dim) + + def _set_transformation_params_to_zero(self): + 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) + + def _calculate_bb_dimension(self, shape, tol=1e-6, triangualte=False, triangulate_tol=1e-1): + """ return the bounding box of the TopoDS_Shape `shape` + Parameters + ---------- + shape : TopoDS_Shape or a subclass such as TopoDS_Face + the shape to compute the bounding box from + tol: float + tolerance of the computed boundingbox + triangualte : bool + 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 + Returns + ------- + tuple: consisting of two tuples: first one has coords of minimum, the second one coords of maximum + """ + bbox = Bnd_Box() + bbox.SetGap(tol) + if triangualte: + BRepMesh_IncrementalMesh(shape, triangulate_tol) + brepbndlib_Add(shape, bbox, triangualte) + 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. - 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_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, 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 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, basestring): - 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.\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\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 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 + """ + 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_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, 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 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, basestring): + 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.\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\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 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/tests/test_ffdparams.py b/tests/test_ffdparams.py index 4ca337b..f594e05 100644 --- a/tests/test_ffdparams.py +++ b/tests/test_ffdparams.py @@ -11,395 +11,395 @@ class TestFFDParameters(TestCase): - def test_class_members_default_n_control_points(self): - params = ffdp.FFDParameters() - assert 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_x(self): - params = ffdp.FFDParameters() - assert params.lenght_box_x == 1. - - def test_class_members_default_lenght_box_y(self): - params = ffdp.FFDParameters() - assert params.lenght_box_y == 1. - - def test_class_members_default_lenght_box_z(self): - params = ffdp.FFDParameters() - assert params.lenght_box_z == 1. - - def test_class_members_default_origin_box(self): - params = ffdp.FFDParameters() - np.testing.assert_array_almost_equal(params.origin_box, np.zeros(3)) - - def test_class_members_default_rot_angle_x(self): - params = ffdp.FFDParameters() - assert params.rot_angle_x == 0 - - def test_class_members_default_rot_angle_y(self): - params = ffdp.FFDParameters() - assert params.rot_angle_y == 0 - - def test_class_members_default_rot_angle_z(self): - params = ffdp.FFDParameters() - assert params.rot_angle_z == 0 - - 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 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 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 params.lenght_box_x == 45.0 - - def test_read_parameters_lenght_box_y(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.lenght_box_y == 90.0 - - def test_read_parameters_lenght_box_z(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.lenght_box_z == 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 params.rot_angle_x == 20.3 - - def test_read_parameters_rot_angle_y(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.rot_angle_y == 11.0 - - def test_read_parameters_rot_angle_z(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.read_parameters('tests/test_datasets/parameters_sphere.prm') - assert params.rot_angle_z == 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)) - 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 - ]).reshape((3, 3)) - 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_info(self): - params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) - params.print_info() - - def test_set_box_origin(self): - origin = np.array([0., 0., 0.]) - - params = ffdp.FFDParameters() - params._set_box_origin(origin) - np.testing.assert_almost_equal(params.origin_box, origin) - - def test_set_box_dimension(self): - origin = np.array([0., 0., 0.]) - tops = np.array([10., 10., 10.]) - params = ffdp.FFDParameters() - params._set_box_origin(origin) - params._set_box_dimensions(origin, tops) - self.assertEqual(params.lenght_box_x, tops[0]) - self.assertEqual(params.lenght_box_y, tops[1]) - self.assertEqual(params.lenght_box_z, tops[2]) - - 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._set_box_origin(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._set_box_origin(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, triangualte=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 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_x(self): + params = ffdp.FFDParameters() + assert params.lenght_box_x == 1. + + def test_class_members_default_lenght_box_y(self): + params = ffdp.FFDParameters() + assert params.lenght_box_y == 1. + + def test_class_members_default_lenght_box_z(self): + params = ffdp.FFDParameters() + assert params.lenght_box_z == 1. + + def test_class_members_default_origin_box(self): + params = ffdp.FFDParameters() + np.testing.assert_array_almost_equal(params.origin_box, np.zeros(3)) + + def test_class_members_default_rot_angle_x(self): + params = ffdp.FFDParameters() + assert params.rot_angle_x == 0 + + def test_class_members_default_rot_angle_y(self): + params = ffdp.FFDParameters() + assert params.rot_angle_y == 0 + + def test_class_members_default_rot_angle_z(self): + params = ffdp.FFDParameters() + assert params.rot_angle_z == 0 + + 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 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 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 params.lenght_box_x == 45.0 + + def test_read_parameters_lenght_box_y(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.lenght_box_y == 90.0 + + def test_read_parameters_lenght_box_z(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.lenght_box_z == 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 params.rot_angle_x == 20.3 + + def test_read_parameters_rot_angle_y(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.rot_angle_y == 11.0 + + def test_read_parameters_rot_angle_z(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.read_parameters('tests/test_datasets/parameters_sphere.prm') + assert params.rot_angle_z == 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)) + 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 + ]).reshape((3, 3)) + 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_info(self): + params = ffdp.FFDParameters(n_control_points=[3, 2, 2]) + params.print_info() + + def test_set_box_origin(self): + origin = np.array([0., 0., 0.]) + + params = ffdp.FFDParameters() + params._set_box_origin(origin) + np.testing.assert_almost_equal(params.origin_box, origin) + + def test_set_box_dimension(self): + origin = np.array([0., 0., 0.]) + tops = np.array([10., 10., 10.]) + params = ffdp.FFDParameters() + params._set_box_origin(origin) + params._set_box_dimensions(origin, tops) + self.assertEqual(params.lenght_box_x, tops[0]) + self.assertEqual(params.lenght_box_y, tops[1]) + self.assertEqual(params.lenght_box_z, tops[2]) + + 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._set_box_origin(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._set_box_origin(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, triangualte=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) From 5ee039038934608d0e4c51c0d5f7cf198619b144 Mon Sep 17 00:00:00 2001 From: Maciej Grochowicz Date: Mon, 24 Oct 2016 12:52:31 +0200 Subject: [PATCH 6/8] Rebase master, fix space-tabs issue. --- pygem/params.py | 156 ++++++++++++++++++++++++------------------------ 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/pygem/params.py b/pygem/params.py index a49ba2e..32d872c 100644 --- a/pygem/params.py +++ b/pygem/params.py @@ -17,9 +17,8 @@ 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 x_ctrl_pnts (int): Number of control points along OX axis - :param y_ctrl_pnts (int): Number of control points along OY axis - :param z_ctrl_pnts (int): Number of control points along OZ axis + :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). @@ -65,12 +64,16 @@ class FFDParameters(object): >>> params1 = ffdp.FFDParameters() >>> params1.read_parameters(filename='tests/test_datasets/parameters_test_ffd_identity.prm') + >>> # Creating a defaul paramters 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') + .. 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. @@ -85,13 +88,17 @@ def __init__(self, n_control_points=None): self.rot_angle_z = 0. if n_control_points is None: - self.n_control_points = [2, 2, 2] - else: - self.n_control_points = n_control_points + n_control_points = [2, 2, 2] + self.n_control_points = n_control_points - self._set_transformation_params_to_zero() + self.array_mu_x = np.zeros((self.n_control_points[0], self.n_control_points[1], \ + self.n_control_points[2])) + self.array_mu_y = np.zeros((self.n_control_points[0], self.n_control_points[1], \ + self.n_control_points[2])) + self.array_mu_z = np.zeros((self.n_control_points[0], self.n_control_points[1], \ + self.n_control_points[2])) - self.psi_mapping = np.diag([1. / self.lenght_box_x, 1. / self.lenght_box_y, 1. / self.lenght_box_z]) + self.psi_mapping = np.diag([1./self.lenght_box_x, 1./self.lenght_box_y, 1./self.lenght_box_z]) self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) self.rotation_matrix = np.eye(3) @@ -99,9 +106,7 @@ def __init__(self, n_control_points=None): 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.]) - self.array_mu_x = np.zeros(shape=self.n_control_points) - self.array_mu_y = np.zeros(shape=self.n_control_points) - self.array_mu_z = np.zeros(shape=self.n_control_points) + def read_parameters(self, filename='parameters.prm'): """ @@ -116,7 +121,7 @@ def read_parameters(self, filename='parameters.prm'): if not os.path.isfile(filename): self.write_parameters(filename) return - + config = ConfigParser.RawConfigParser() config.read(filename) @@ -137,46 +142,50 @@ def read_parameters(self, filename='parameters.prm'): self.rot_angle_y = config.getfloat('Box info', 'rotation angle y') self.rot_angle_z = config.getfloat('Box info', 'rotation angle z') - self.array_mu_x = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + self.array_mu_x = np.zeros((self.n_control_points[0], self.n_control_points[1], \ + self.n_control_points[2])) mux = config.get('Parameters weights', 'parameter x') lines = mux.split('\n') for line in lines: values = line.split() self.array_mu_x[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - self.array_mu_y = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + self.array_mu_y = np.zeros((self.n_control_points[0], self.n_control_points[1], \ + self.n_control_points[2])) muy = config.get('Parameters weights', 'parameter y') lines = muy.split('\n') for line in lines: values = line.split() self.array_mu_y[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - self.array_mu_z = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + self.array_mu_z = np.zeros((self.n_control_points[0], self.n_control_points[1], \ + self.n_control_points[2])) muz = config.get('Parameters weights', 'parameter z') lines = muz.split('\n') for line in lines: values = line.split() self.array_mu_z[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - self.rotation_matrix = at.angles2matrix( - self.rot_angle_z * np.pi / 180, - self.rot_angle_y * np.pi / 180, - self.rot_angle_x * np.pi / 180 - ) + self.rotation_matrix = at.angles2matrix(self.rot_angle_z * np.pi/180, \ + self.rot_angle_y * np.pi/180, self.rot_angle_x * np.pi/180) self.position_vertex_0 = self.origin_box - self.position_vertex_1 = self.position_vertex_0 + np.dot(self.rotation_matrix, [self.lenght_box_x, 0, 0]) - self.position_vertex_2 = self.position_vertex_0 + np.dot(self.rotation_matrix, [0, self.lenght_box_y, 0]) - self.position_vertex_3 = self.position_vertex_0 + np.dot(self.rotation_matrix, [0, 0, self.lenght_box_z]) - - self.psi_mapping = np.diag([1. / self.lenght_box_x, 1. / self.lenght_box_y, 1. / self.lenght_box_z]) + self.position_vertex_1 = self.position_vertex_0 + \ + np.dot(self.rotation_matrix, [self.lenght_box_x, 0, 0]) + self.position_vertex_2 = self.position_vertex_0 + \ + np.dot(self.rotation_matrix, [0, self.lenght_box_y, 0]) + self.position_vertex_3 = self.position_vertex_0 + \ + np.dot(self.rotation_matrix, [0, 0, self.lenght_box_z]) + + self.psi_mapping = np.diag([1./self.lenght_box_x, 1./self.lenght_box_y, 1./self.lenght_box_z]) self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) + 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, basestring): @@ -186,39 +195,30 @@ def write_parameters(self, filename='parameters.prm'): output_file.write('\n[Box info]\n') output_file.write('# This section collects all the properties of the FFD bounding box.\n') - output_file.write( - '\n# n control points indicates the number of control points in each direction (x, y, z).\n') - output_file.write( - '# For example, to create a 2 x 3 x 2 grid, use the following: n control points: 2, 3, 2\n') + output_file.write('\n# n control points indicates the number of control points in each direction (x, y, z).\n') + output_file.write('# For example, to create a 2 x 3 x 2 grid, use the following: n control points: 2, 3, 2\n') output_file.write('n control points x: ' + str(self.n_control_points[0]) + '\n') output_file.write('n control points y: ' + str(self.n_control_points[1]) + '\n') output_file.write('n control points z: ' + str(self.n_control_points[2]) + '\n') - output_file.write( - '\n# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).\n') + output_file.write('\n# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).\n') output_file.write('# It uses the local coordinate system.\n') - output_file.write( - '# 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_file.write('# 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_file.write('box lenght x: ' + str(self.lenght_box_x) + '\n') output_file.write('box lenght y: ' + str(self.lenght_box_y) + '\n') output_file.write('box lenght z: ' + str(self.lenght_box_z) + '\n') - output_file.write( - '\n# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of\n') - output_file.write( - '# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].\n') + output_file.write('\n# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of\n') + output_file.write('# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].\n') output_file.write('# See section "Parameters weights" for more details.\n') - output_file.write( - '# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.\n') + output_file.write('# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.\n') output_file.write('box origin x: ' + str(self.origin_box[0]) + '\n') output_file.write('box origin y: ' + str(self.origin_box[1]) + '\n') output_file.write('box origin z: ' + str(self.origin_box[2]) + '\n') - output_file.write( - '\n# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.\n') + output_file.write('\n# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.\n') output_file.write('# The rotation is done with respect to the box origin.\n') - output_file.write( - '# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.\n') + output_file.write('# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.\n') output_file.write('rotation angle x: ' + str(self.rot_angle_x) + '\n') output_file.write('rotation angle y: ' + str(self.rot_angle_y) + '\n') output_file.write('rotation angle z: ' + str(self.rot_angle_z) + '\n') @@ -226,18 +226,16 @@ def write_parameters(self, filename='parameters.prm'): output_file.write('\n\n[Parameters weights]\n') output_file.write('# This section describes the weights of the FFD control points.\n') output_file.write('# We adopt the following convention:\n') - output_file.write( - '# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.\n') + output_file.write('# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.\n') output_file.write('# If a weight is equal to zero you can discard the line since the default is zero.\n') output_file.write('#\n') output_file.write('# | x index | y index | z index | weight |\n') output_file.write('# --------------------------------------\n') output_file.write('# | 0 | 0 | 0 | 1.0 |\n') - output_file.write( - '# | 0 | 1 | 1 | 0.0 | --> you can erase this line without effects\n') + output_file.write('# | 0 | 1 | 1 | 0.0 | --> you can erase this line without effects\n') output_file.write('# | 0 | 1 | 0 | -2.1 |\n') output_file.write('# | 0 | 0 | 1 | 3.4 |\n') - + output_file.write('\n# parameter x collects the displacements along x, normalized with the box lenght x.') output_file.write('\nparameter x:') offset = 1 @@ -245,7 +243,7 @@ def write_parameters(self, filename='parameters.prm'): for j in range(0, self.n_control_points[1]): for k in range(0, self.n_control_points[2]): output_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_x[i][j][k]) + '\n') + ' ' + str(self.array_mu_x[i][j][k]) + '\n') offset = 13 output_file.write('\n# parameter y collects the displacements along y, normalized with the box lenght y.') @@ -255,7 +253,7 @@ def write_parameters(self, filename='parameters.prm'): for j in range(0, self.n_control_points[1]): for k in range(0, self.n_control_points[2]): output_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_y[i][j][k]) + '\n') + ' ' + str(self.array_mu_y[i][j][k]) + '\n') offset = 13 output_file.write('\n# parameter z collects the displacements along z, normalized with the box lenght z.') @@ -265,20 +263,21 @@ def write_parameters(self, filename='parameters.prm'): for j in range(0, self.n_control_points[1]): for k in range(0, self.n_control_points[2]): output_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_z[i][j][k]) + '\n') + ' ' + str(self.array_mu_z[i][j][k]) + '\n') offset = 13 + def print_info(self): """ This method prints all the FFD parameters on the screen. Its purpose is for debugging. """ 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 =' @@ -395,7 +394,6 @@ class RBFParameters(object): coordinates of the interpolation control points after the deformation. The default value is None. """ - def __init__(self): self.basis = None self.radius = None @@ -403,6 +401,7 @@ def __init__(self): 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. @@ -420,12 +419,12 @@ def read_parameters(self, filename='parameters_rbf.prm'): 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)) + 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)) + 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) @@ -445,19 +444,19 @@ def read_parameters(self, filename='parameters_rbf.prm'): 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)) + " 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 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, basestring): @@ -468,40 +467,40 @@ def write_parameters(self, filename='parameters_rbf.prm'): 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') + '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.\n') + output_file.write('# inv_multi_quadratic_biharmonic_spline, thin_plate_spline, beckert_wendland_c2_basis.\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') + '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\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') + '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(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') + '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 + 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 print_info(self): """ @@ -514,3 +513,4 @@ def print_info(self): print self.original_control_points print '\ndeformed_control_points =' print self.deformed_control_points + From 36090f93a5a56cfdff0b68e89c79a15ac7a55dcf Mon Sep 17 00:00:00 2001 From: Maciej Grochowicz Date: Mon, 24 Oct 2016 12:55:12 +0200 Subject: [PATCH 7/8] Rebase master and fix spaces-tabs issue --- .idea/inspectionProfiles/Project_Default.xml | 12 ++++++++++++ .idea/inspectionProfiles/profiles_settings.xml | 7 +++++++ 2 files changed, 19 insertions(+) create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..d4e3d0a --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..3b31283 --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file From 2e9b5f6235cca897e3c9f0b152d1fbde95ffc879 Mon Sep 17 00:00:00 2001 From: Maciej Grochowicz Date: Mon, 24 Oct 2016 13:06:55 +0200 Subject: [PATCH 8/8] Rebase master, fix intendations --- pygem/params.py | 156 ++++++++++++++++++++++++------------------------ 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/pygem/params.py b/pygem/params.py index a49ba2e..32d872c 100644 --- a/pygem/params.py +++ b/pygem/params.py @@ -17,9 +17,8 @@ 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 x_ctrl_pnts (int): Number of control points along OX axis - :param y_ctrl_pnts (int): Number of control points along OY axis - :param z_ctrl_pnts (int): Number of control points along OZ axis + :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). @@ -65,12 +64,16 @@ class FFDParameters(object): >>> params1 = ffdp.FFDParameters() >>> params1.read_parameters(filename='tests/test_datasets/parameters_test_ffd_identity.prm') + >>> # Creating a defaul paramters 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') + .. 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. @@ -85,13 +88,17 @@ def __init__(self, n_control_points=None): self.rot_angle_z = 0. if n_control_points is None: - self.n_control_points = [2, 2, 2] - else: - self.n_control_points = n_control_points + n_control_points = [2, 2, 2] + self.n_control_points = n_control_points - self._set_transformation_params_to_zero() + self.array_mu_x = np.zeros((self.n_control_points[0], self.n_control_points[1], \ + self.n_control_points[2])) + self.array_mu_y = np.zeros((self.n_control_points[0], self.n_control_points[1], \ + self.n_control_points[2])) + self.array_mu_z = np.zeros((self.n_control_points[0], self.n_control_points[1], \ + self.n_control_points[2])) - self.psi_mapping = np.diag([1. / self.lenght_box_x, 1. / self.lenght_box_y, 1. / self.lenght_box_z]) + self.psi_mapping = np.diag([1./self.lenght_box_x, 1./self.lenght_box_y, 1./self.lenght_box_z]) self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) self.rotation_matrix = np.eye(3) @@ -99,9 +106,7 @@ def __init__(self, n_control_points=None): 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.]) - self.array_mu_x = np.zeros(shape=self.n_control_points) - self.array_mu_y = np.zeros(shape=self.n_control_points) - self.array_mu_z = np.zeros(shape=self.n_control_points) + def read_parameters(self, filename='parameters.prm'): """ @@ -116,7 +121,7 @@ def read_parameters(self, filename='parameters.prm'): if not os.path.isfile(filename): self.write_parameters(filename) return - + config = ConfigParser.RawConfigParser() config.read(filename) @@ -137,46 +142,50 @@ def read_parameters(self, filename='parameters.prm'): self.rot_angle_y = config.getfloat('Box info', 'rotation angle y') self.rot_angle_z = config.getfloat('Box info', 'rotation angle z') - self.array_mu_x = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + self.array_mu_x = np.zeros((self.n_control_points[0], self.n_control_points[1], \ + self.n_control_points[2])) mux = config.get('Parameters weights', 'parameter x') lines = mux.split('\n') for line in lines: values = line.split() self.array_mu_x[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - self.array_mu_y = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + self.array_mu_y = np.zeros((self.n_control_points[0], self.n_control_points[1], \ + self.n_control_points[2])) muy = config.get('Parameters weights', 'parameter y') lines = muy.split('\n') for line in lines: values = line.split() self.array_mu_y[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - self.array_mu_z = np.zeros((self.n_control_points[0], self.n_control_points[1], self.n_control_points[2])) + self.array_mu_z = np.zeros((self.n_control_points[0], self.n_control_points[1], \ + self.n_control_points[2])) muz = config.get('Parameters weights', 'parameter z') lines = muz.split('\n') for line in lines: values = line.split() self.array_mu_z[int(values[0])][int(values[1])][int(values[2])] = float(values[3]) - self.rotation_matrix = at.angles2matrix( - self.rot_angle_z * np.pi / 180, - self.rot_angle_y * np.pi / 180, - self.rot_angle_x * np.pi / 180 - ) + self.rotation_matrix = at.angles2matrix(self.rot_angle_z * np.pi/180, \ + self.rot_angle_y * np.pi/180, self.rot_angle_x * np.pi/180) self.position_vertex_0 = self.origin_box - self.position_vertex_1 = self.position_vertex_0 + np.dot(self.rotation_matrix, [self.lenght_box_x, 0, 0]) - self.position_vertex_2 = self.position_vertex_0 + np.dot(self.rotation_matrix, [0, self.lenght_box_y, 0]) - self.position_vertex_3 = self.position_vertex_0 + np.dot(self.rotation_matrix, [0, 0, self.lenght_box_z]) - - self.psi_mapping = np.diag([1. / self.lenght_box_x, 1. / self.lenght_box_y, 1. / self.lenght_box_z]) + self.position_vertex_1 = self.position_vertex_0 + \ + np.dot(self.rotation_matrix, [self.lenght_box_x, 0, 0]) + self.position_vertex_2 = self.position_vertex_0 + \ + np.dot(self.rotation_matrix, [0, self.lenght_box_y, 0]) + self.position_vertex_3 = self.position_vertex_0 + \ + np.dot(self.rotation_matrix, [0, 0, self.lenght_box_z]) + + self.psi_mapping = np.diag([1./self.lenght_box_x, 1./self.lenght_box_y, 1./self.lenght_box_z]) self.inv_psi_mapping = np.diag([self.lenght_box_x, self.lenght_box_y, self.lenght_box_z]) + 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, basestring): @@ -186,39 +195,30 @@ def write_parameters(self, filename='parameters.prm'): output_file.write('\n[Box info]\n') output_file.write('# This section collects all the properties of the FFD bounding box.\n') - output_file.write( - '\n# n control points indicates the number of control points in each direction (x, y, z).\n') - output_file.write( - '# For example, to create a 2 x 3 x 2 grid, use the following: n control points: 2, 3, 2\n') + output_file.write('\n# n control points indicates the number of control points in each direction (x, y, z).\n') + output_file.write('# For example, to create a 2 x 3 x 2 grid, use the following: n control points: 2, 3, 2\n') output_file.write('n control points x: ' + str(self.n_control_points[0]) + '\n') output_file.write('n control points y: ' + str(self.n_control_points[1]) + '\n') output_file.write('n control points z: ' + str(self.n_control_points[2]) + '\n') - output_file.write( - '\n# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).\n') + output_file.write('\n# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).\n') output_file.write('# It uses the local coordinate system.\n') - output_file.write( - '# 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_file.write('# 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_file.write('box lenght x: ' + str(self.lenght_box_x) + '\n') output_file.write('box lenght y: ' + str(self.lenght_box_y) + '\n') output_file.write('box lenght z: ' + str(self.lenght_box_z) + '\n') - output_file.write( - '\n# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of\n') - output_file.write( - '# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].\n') + output_file.write('\n# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of\n') + output_file.write('# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].\n') output_file.write('# See section "Parameters weights" for more details.\n') - output_file.write( - '# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.\n') + output_file.write('# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.\n') output_file.write('box origin x: ' + str(self.origin_box[0]) + '\n') output_file.write('box origin y: ' + str(self.origin_box[1]) + '\n') output_file.write('box origin z: ' + str(self.origin_box[2]) + '\n') - output_file.write( - '\n# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.\n') + output_file.write('\n# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.\n') output_file.write('# The rotation is done with respect to the box origin.\n') - output_file.write( - '# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.\n') + output_file.write('# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.\n') output_file.write('rotation angle x: ' + str(self.rot_angle_x) + '\n') output_file.write('rotation angle y: ' + str(self.rot_angle_y) + '\n') output_file.write('rotation angle z: ' + str(self.rot_angle_z) + '\n') @@ -226,18 +226,16 @@ def write_parameters(self, filename='parameters.prm'): output_file.write('\n\n[Parameters weights]\n') output_file.write('# This section describes the weights of the FFD control points.\n') output_file.write('# We adopt the following convention:\n') - output_file.write( - '# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.\n') + output_file.write('# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.\n') output_file.write('# If a weight is equal to zero you can discard the line since the default is zero.\n') output_file.write('#\n') output_file.write('# | x index | y index | z index | weight |\n') output_file.write('# --------------------------------------\n') output_file.write('# | 0 | 0 | 0 | 1.0 |\n') - output_file.write( - '# | 0 | 1 | 1 | 0.0 | --> you can erase this line without effects\n') + output_file.write('# | 0 | 1 | 1 | 0.0 | --> you can erase this line without effects\n') output_file.write('# | 0 | 1 | 0 | -2.1 |\n') output_file.write('# | 0 | 0 | 1 | 3.4 |\n') - + output_file.write('\n# parameter x collects the displacements along x, normalized with the box lenght x.') output_file.write('\nparameter x:') offset = 1 @@ -245,7 +243,7 @@ def write_parameters(self, filename='parameters.prm'): for j in range(0, self.n_control_points[1]): for k in range(0, self.n_control_points[2]): output_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_x[i][j][k]) + '\n') + ' ' + str(self.array_mu_x[i][j][k]) + '\n') offset = 13 output_file.write('\n# parameter y collects the displacements along y, normalized with the box lenght y.') @@ -255,7 +253,7 @@ def write_parameters(self, filename='parameters.prm'): for j in range(0, self.n_control_points[1]): for k in range(0, self.n_control_points[2]): output_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_y[i][j][k]) + '\n') + ' ' + str(self.array_mu_y[i][j][k]) + '\n') offset = 13 output_file.write('\n# parameter z collects the displacements along z, normalized with the box lenght z.') @@ -265,20 +263,21 @@ def write_parameters(self, filename='parameters.prm'): for j in range(0, self.n_control_points[1]): for k in range(0, self.n_control_points[2]): output_file.write(offset * ' ' + str(i) + ' ' + str(j) + ' ' + str(k) + \ - ' ' + str(self.array_mu_z[i][j][k]) + '\n') + ' ' + str(self.array_mu_z[i][j][k]) + '\n') offset = 13 + def print_info(self): """ This method prints all the FFD parameters on the screen. Its purpose is for debugging. """ 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 =' @@ -395,7 +394,6 @@ class RBFParameters(object): coordinates of the interpolation control points after the deformation. The default value is None. """ - def __init__(self): self.basis = None self.radius = None @@ -403,6 +401,7 @@ def __init__(self): 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. @@ -420,12 +419,12 @@ def read_parameters(self, filename='parameters_rbf.prm'): 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)) + 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)) + 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) @@ -445,19 +444,19 @@ def read_parameters(self, filename='parameters_rbf.prm'): 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)) + " 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 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, basestring): @@ -468,40 +467,40 @@ def write_parameters(self, filename='parameters_rbf.prm'): 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') + '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.\n') + output_file.write('# inv_multi_quadratic_biharmonic_spline, thin_plate_spline, beckert_wendland_c2_basis.\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') + '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\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') + '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(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') + '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 + 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 print_info(self): """ @@ -514,3 +513,4 @@ def print_info(self): print self.original_control_points print '\ndeformed_control_points =' print self.deformed_control_points +