Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ install:
- conda create --yes -n test python=$TRAVIS_PYTHON_VERSION
- source activate test
- conda install --yes numpy scipy matplotlib pip nose vtk
- conda install --yes -c https://conda.anaconda.org/dlr-sc pythonocc-core
- pip install setuptools
- pip install enum34
- pip install numpy-stl
Expand Down
1 change: 1 addition & 0 deletions docs/source/code.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ Code Documentation
stlhandler
vtkhandler
unvhandler
igeshandler

9 changes: 9 additions & 0 deletions docs/source/igeshandler.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Igeshandler
=================

.. automodule:: pygem.igeshandler

.. autoclass:: IgesHandler
:members:
:private-members:
:special-members:
3 changes: 2 additions & 1 deletion pygem/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

__all__ = ['affine', 'filehandler', 'freeform', 'openfhandler', 'params', 'stlhandler', 'unvhandler', 'vtkhandler']
__all__ = ['affine', 'filehandler', 'freeform', 'openfhandler', 'params', 'stlhandler', 'unvhandler', 'vtkhandler', 'igeshandler']

from . import affine
from . import freeform
Expand All @@ -9,3 +9,4 @@
from . import stlhandler
from . import unvhandler
from . import vtkhandler
from . import igeshandler
226 changes: 226 additions & 0 deletions pygem/igeshandler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
"""
Utilities for reading and writing different CAD files.
"""
import numpy as np
import pygem.filehandler as fh
from OCC.IGESControl import (IGESControl_Reader, IGESControl_Writer)
from OCC.BRep import BRep_Tool
from OCC.BRepBuilderAPI import (BRepBuilderAPI_NurbsConvert, BRepBuilderAPI_MakeWire, BRepBuilderAPI_MakeEdge, BRepBuilderAPI_MakeFace)
from OCC.GeomConvert import geomconvert_SurfaceToBSplineSurface
import OCC.TopoDS
from OCC.TopAbs import (TopAbs_FACE, TopAbs_EDGE)
from OCC.TopExp import TopExp_Explorer
from OCC.Geom import Geom_BSplineSurface
from OCC.gp import (gp_Pnt, gp_XYZ)
from OCC.Display.SimpleGui import init_display
from OCC.ShapeFix import ShapeFix_ShapeTolerance


class IgesHandler(fh.FileHandler):
"""
Iges file handler class

:cvar string infile: name of the input file to be processed.
:cvar string outfile: name of the output file where to write in.
:cvar string extension: extension of the input/output files. It is equal to '.iges'.
:cvar list control_point_position: index of the first NURBS control point (or pole) of each face of the iges file.
"""
def __init__(self):
super(IgesHandler, self).__init__()
self.extension = '.iges' # TODO: also igs could be accepted
self._control_point_position = None


def parse(self, filename):
"""
Method to parse the file `filename`. It returns a matrix with all the coordinates.

:return: mesh_points: it is a `n_points`-by-3 matrix containing the coordinates of
the points of the mesh
:rtype: numpy.ndarray

.. todo::

- specify when it works
"""
self._check_filename_type(filename)
self._check_extension(filename)

self.infile = filename

## read in the IGES file
reader = IGESControl_Reader()
reader.ReadFile(self.infile)
reader.TransferRoots()
shape = reader.Shape()

## cycle on the faces to get the control points
# init some quantities
n_faces = 0
control_point_position = [0]
faces_explorer = TopExp_Explorer(shape, TopAbs_FACE)
mesh_points = np.zeros(shape=(0,3))

while faces_explorer.More():

# performing some conversions to get the right format (BSplineSurface)
iges_face = OCC.TopoDS.topods_Face(faces_explorer.Current())
iges_nurbs_converter = BRepBuilderAPI_NurbsConvert(iges_face)
iges_nurbs_converter.Perform(iges_face)
nurbs_face = iges_nurbs_converter.Shape()
brep_face = BRep_Tool.Surface(OCC.TopoDS.topods_Face(nurbs_face))
bspline_face = geomconvert_SurfaceToBSplineSurface(brep_face)

# openCascade object
occ_face = bspline_face.GetObject()

# extract the Control Points of each face
n_poles_u = occ_face.NbUPoles()
n_poles_v = occ_face.NbVPoles()
control_polygon_coordinates = np.zeros(shape=(n_poles_u*n_poles_v,3))

# cycle over the poles to get their coordinates
i = 0
for pole_u_direction in xrange(n_poles_u):
for pole_v_direction in xrange(n_poles_v):
control_point_coordinates = occ_face.Pole(pole_u_direction+1,pole_v_direction+1)
weight = occ_face.Weight(pole_u_direction+1,pole_v_direction+1)
control_polygon_coordinates[i,:] = [control_point_coordinates.X(), control_point_coordinates.Y(), control_point_coordinates.Z()]
i += 1

# pushing the control points coordinates to the mesh_points array (used for FFD)
mesh_points = np.append(mesh_points, control_polygon_coordinates, axis=0)
control_point_position.append(control_point_position[-1] + n_poles_u*n_poles_v)

n_faces += 1
faces_explorer.Next()

print control_point_position

self._control_point_position = control_point_position

return mesh_points


def write(self, mesh_points, filename):
"""
Writes a iges file, called filename, copying all the structures from self.filename but
the coordinates. mesh_points is a matrix that contains the new coordinates to
write in the iges file.

:param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix containing
the coordinates of the points of the mesh
:param string filename: name of the output file.
"""
self._check_filename_type(filename)
self._check_extension(filename)
self._check_infile_instantiation(self.infile)

self.outfile = filename

## init the ouput file writer
writer = IGESControl_Writer()

## read in the IGES file
reader = IGESControl_Reader()
reader.ReadFile(self.infile)
reader.TransferRoots()
shape_read = reader.Shape()

## cycle on the faces to update the control points position
# init some quantities
faces_explorer = TopExp_Explorer(shape_read, TopAbs_FACE)
n_faces = 0
control_point_position = self._control_point_position

while faces_explorer.More():

# similar to the parser method
iges_face = OCC.TopoDS.topods_Face(faces_explorer.Current())
iges_nurbs_converter = BRepBuilderAPI_NurbsConvert(iges_face)
iges_nurbs_converter.Perform(iges_face)
nurbs_face = iges_nurbs_converter.Shape()
face_aux = OCC.TopoDS.topods_Face(nurbs_face)
brep_face = BRep_Tool.Surface(OCC.TopoDS.topods_Face(nurbs_face))
bspline_face = geomconvert_SurfaceToBSplineSurface(brep_face)
occ_face = bspline_face.GetObject()

n_poles_u = occ_face.NbUPoles()
n_poles_v = occ_face.NbVPoles()

i = 0
for pole_u_direction in xrange(n_poles_u):
for pole_v_direction in xrange(n_poles_v):
control_point_coordinates = mesh_points[i+control_point_position[n_faces],:]
point_xyz = gp_XYZ(control_point_coordinates[0], control_point_coordinates[1], control_point_coordinates[2])
gp_point = gp_Pnt(point_xyz)
occ_face.SetPole(pole_u_direction+1,pole_v_direction+1,gp_point)
i += 1

## construct the deformed wire for the trimmed surfaces
wireMaker = BRepBuilderAPI_MakeWire()
tol = ShapeFix_ShapeTolerance()
brep = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), 1e-4).Face()
brep_face = BRep_Tool.Surface(brep)

# cycle on the edges
edge_explorer = TopExp_Explorer(nurbs_face, TopAbs_EDGE)
while edge_explorer.More():
edge = OCC.TopoDS.topods_Edge(edge_explorer.Current())
# edge in the (u,v) coordinates
edge_uv_coordinates = OCC.BRep.BRep_Tool.CurveOnSurface(edge, face_aux)
# evaluating the new edge: same (u,v) coordinates, but different (x,y,x) ones
edge_phis_coordinates_aux = BRepBuilderAPI_MakeEdge(edge_uv_coordinates[0], brep_face)
edge_phis_coordinates = edge_phis_coordinates_aux.Edge()
tol.SetTolerance(edge_phis_coordinates, 1e-4)
wireMaker.Add(edge_phis_coordinates)
edge_explorer.Next()

#grouping the edges in a wire
wire = wireMaker.Wire()

## trimming the surfaces
brep_surf = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), wire).Face()
writer.AddShape(brep_surf)

#print writer

n_faces += 1
faces_explorer.Next()

## write out the iges file
writer.Write(self.outfile)


def plot(self, plot_file=None, save_fig=False):
"""
Method to plot an iges file. If `plot_file` is not given it plots `self.infile`.

:param string plot_file: the iges filename you want to plot.
:param bool save_fig: a flag to save the figure in png or not. If True the
plot is not shown.

.. warning::
It does not work well up to now
"""
if plot_file is None:
plot_file = self.infile
else:
self._check_filename_type(plot_file)

## read in the IGES file
reader = IGESControl_Reader()
reader.ReadFile(plot_file)
reader.TransferRoots()
shape = reader.Shape()

display, start_display, add_menu, add_function_to_menu = init_display()
display.FitAll()
display.DisplayShape(shape, update=True)

# Show the plot to the screen
if not save_fig:
start_display()
else:
display.View.Dump(plot_file.split('.')[0] + '.ppm')

75 changes: 75 additions & 0 deletions tests/test_datasets/parameters_test_ffd_iges.prm
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@

[Box info]
# This section collects all the properties of the FFD bounding box.

# n control points indicates the number of control points in each direction (x, y, z).
# For example, to create a 2 x 3 x 2 grid, use the following: n control points: 2, 3, 2
n control points x: 2
n control points y: 2
n control points z: 2

# box lenght indicates the length of the FFD bounding box along the three canonical directions (x, y, z).
# It uses the local coordinate system.
# For example to create a 2 x 1.5 x 3 meters box use the following: lenght box: 2.0, 1.5, 3.0
box lenght x: 2200.0
box lenght y: 2200.0
box lenght z: 5100.0

# box origin indicates the x, y, and z coordinates of the origin of the FFD bounding box. That is center of
# rotation of the bounding box. It corresponds to the point coordinates with position [0][0][0].
# See section "Parameters weights" for more details.
# For example, if the origin is equal to 0., 0., 0., use the following: origin box: 0., 0., 0.
box origin x: -1100.0
box origin y: -1100.0
box origin z: 5000.0

# rotation angle indicates the rotation angle around the x, y, and z axis of the FFD bounding box in degrees.
# The rotation is done with respect to the box origin.
# For example, to rotate the box by 2 deg along the z direction, use the following: rotation angle: 0., 0., 2.
rotation angle x: 0
rotation angle y: 0
rotation angle z: 0


[Parameters weights]
# This section describes the weights of the FFD control points.
# We adopt the following convention:
# For example with a 2x2x2 grid of control points we have to fill a 2x2x2 matrix of weights.
# If a weight is equal to zero you can discard the line since the default is zero.
#
# | x index | y index | z index | weight |
# --------------------------------------
# | 0 | 0 | 0 | 1.0 |
# | 0 | 1 | 1 | 0.0 | --> you can erase this line without effects
# | 0 | 1 | 0 | -2.1 |
# | 0 | 0 | 1 | 3.4 |

# parameter x collects the displacements along x, normalized with the box lenght x.
parameter x: 0 0 0 0.0
0 0 1 0.0
0 1 0 0.0
0 1 1 0.0
1 0 0 0.0
1 0 1 0.0
1 1 0 0.0
1 1 1 0.3

# parameter y collects the displacements along y, normalized with the box lenght y.
parameter y: 0 0 0 0.0
0 0 1 0.0
0 1 0 0.0
0 1 1 0.0
1 0 0 0.0
1 0 1 0.0
1 1 0 0.0
1 1 1 0.4

# parameter z collects the displacements along z, normalized with the box lenght z.
parameter z: 0 0 0 0.0
0 0 1 0.5
0 1 0 0.0
0 1 1 0.5
1 0 0 0.0
1 0 1 0.2
1 1 0 0.0
1 1 1 0.2
Loading