Skip to content

Commit f6e6d1f

Browse files
authored
Merge pull request #119 from ndem0/stlhandler
Implement `StlHandler` using VTK library
2 parents b390b6a + ace10f9 commit f6e6d1f

File tree

5 files changed

+12173
-12085
lines changed

5 files changed

+12173
-12085
lines changed

.travis.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@ install:
5252
- conda install --yes numpy scipy matplotlib vtk pip nose sip=4.18
5353
- conda install --yes -c https://conda.anaconda.org/dlr-sc pythonocc-core==0.17
5454
- pip install setuptools
55-
- pip install enum34
56-
- pip install numpy-stl
5755
- pip install coveralls
5856
- pip install coverage
5957
- python setup.py install

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Pick the geometry, the parameters file, set the name of the output and decide wh
3030

3131

3232
## Dependencies and installation
33-
**PyGeM** requires `numpy`, `numpy-stl`, `scipy` and `matplotlib`. They can be easily installed via `pip`.
33+
**PyGeM** requires `numpy`, `scipy` and `matplotlib`. They can be easily installed via `pip`.
3434
Moreover **PyGeM** depends on `OCC` and `vtk`. These requirements cannot be satisfied through `pip`.
3535
Please see the table below for instructions on how to satisfy the requirements.
3636

pygem/stlhandler.py

Lines changed: 135 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
"""
44
import numpy as np
55
from mpl_toolkits import mplot3d
6-
from matplotlib import pyplot
7-
from stl import mesh, Mode
6+
import matplotlib.pyplot as plt
7+
import mpl_toolkits.mplot3d as a3
88
import pygem.filehandler as fh
9+
import vtk
910

1011

1112
class StlHandler(fh.FileHandler):
@@ -14,7 +15,8 @@ class StlHandler(fh.FileHandler):
1415
1516
:cvar string infile: name of the input file to be processed.
1617
:cvar string outfile: name of the output file where to write in.
17-
:cvar list extensions: extensions of the input/output files. It is equal to ['.stl'].
18+
:cvar list extensions: extensions of the input/output files. It is equal to
19+
['.stl'].
1820
"""
1921

2022
def __init__(self):
@@ -24,94 +26,129 @@ def __init__(self):
2426

2527
def parse(self, filename):
2628
"""
27-
Method to parse the `filename`. It returns a matrix with all the coordinates.
29+
Method to parse the `filename`. It returns a matrix with all the
30+
coordinates.
2831
2932
:param string filename: name of the input file.
3033
31-
:return: mesh_points: it is a `n_points`-by-3 matrix containing the coordinates of
32-
the points of the mesh
34+
:return: mesh_points: it is a `n_points`-by-3 matrix containing the
35+
coordinates of the points of the mesh
3336
:rtype: numpy.ndarray
34-
35-
.. todo::
36-
37-
- specify when it works
3837
"""
3938
self._check_filename_type(filename)
4039
self._check_extension(filename)
4140

4241
self.infile = filename
4342

44-
stl_mesh = mesh.Mesh.from_file(self.infile)
45-
mesh_points = np.array(
46-
[stl_mesh.x.ravel(), stl_mesh.y.ravel(), stl_mesh.z.ravel()]
47-
)
48-
mesh_points = mesh_points.T
43+
reader = vtk.vtkSTLReader()
44+
reader.SetFileName(self.infile)
45+
reader.Update()
46+
data = reader.GetOutput()
47+
48+
n_points = data.GetNumberOfPoints()
49+
mesh_points = np.zeros([n_points, 3])
50+
51+
for i in range(n_points):
52+
mesh_points[i][0], mesh_points[i][1], mesh_points[i][
53+
2
54+
] = data.GetPoint(i)
4955

5056
return mesh_points
5157

5258
def write(self, mesh_points, filename, write_bin=False):
5359
"""
54-
Writes a stl file, called filename, copying all the lines from self.filename but
55-
the coordinates. mesh_points is a matrix that contains the new coordinates to
56-
write in the stl file.
60+
Writes a stl file, called filename, copying all the lines from
61+
self.filename but the coordinates. mesh_points is a matrix that contains
62+
the new coordinates to write in the stl file.
5763
58-
:param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix containing
59-
the coordinates of the points of the mesh.
64+
:param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix
65+
containing the coordinates of the points of the mesh.
6066
:param string filename: name of the output file.
61-
:param boolean write_bin: flag to write in the binary format. Default is False.
67+
:param boolean write_bin: flag to write in the binary format. Default is
68+
False.
6269
"""
6370
self._check_filename_type(filename)
6471
self._check_extension(filename)
6572
self._check_infile_instantiation()
6673

6774
self.outfile = filename
6875

69-
n_vertices = mesh_points.shape[0]
70-
# number of triplets of vertices
71-
n_triplets = n_vertices // 3
72-
data = np.zeros(n_triplets, dtype=mesh.Mesh.dtype)
73-
stl_mesh = mesh.Mesh(data, remove_empty_areas=False)
76+
reader = vtk.vtkSTLReader()
77+
reader.SetFileName(self.infile)
78+
reader.Update()
79+
data = reader.GetOutput()
7480

75-
for i in range(0, n_triplets):
76-
for j in range(0, 3):
77-
data['vectors'][i][j] = mesh_points[3 * i + j]
81+
points = vtk.vtkPoints()
82+
83+
for i in range(data.GetNumberOfPoints()):
84+
points.InsertNextPoint(mesh_points[i, :])
85+
86+
data.SetPoints(points)
7887

79-
if not write_bin:
80-
stl_mesh.save(self.outfile, mode=Mode.ASCII, update_normals=True)
88+
writer = vtk.vtkSTLWriter()
89+
writer.SetFileName(self.outfile)
90+
91+
if vtk.VTK_MAJOR_VERSION <= 5:
92+
writer.SetInput(data)
8193
else:
82-
stl_mesh.save(self.outfile, update_normals=True)
94+
writer.SetInputData(data)
95+
96+
if write_bin:
97+
writer.SetFileTypeToBinary()
98+
else:
99+
writer.SetFileTypeToASCII()
100+
101+
writer.Write()
83102

84103
def plot(self, plot_file=None, save_fig=False):
85104
"""
86-
Method to plot an stl file. If `plot_file` is not given it plots `self.infile`.
105+
Method to plot an stl file. If `plot_file` is not given it plots
106+
`self.infile`.
87107
88108
:param string plot_file: the stl filename you want to plot.
89-
:param bool save_fig: a flag to save the figure in png or not. If True the
90-
plot is not shown. The default value is False.
109+
:param bool save_fig: a flag to save the figure in png or not. If True
110+
the plot is not shown. The default value is False.
91111
92-
:return: figure: matlplotlib structure for the figure of the chosen geometry
112+
:return: figure: matlplotlib structure for the figure of the chosen
113+
geometry
93114
:rtype: matplotlib.pyplot.figure
94115
"""
95116
if plot_file is None:
96117
plot_file = self.infile
97118
else:
98119
self._check_filename_type(plot_file)
99120

100-
# Create a new plot
101-
figure = pyplot.figure()
102-
axes = mplot3d.Axes3D(figure)
121+
# Read the source file.
122+
reader = vtk.vtkSTLReader()
123+
reader.SetFileName(plot_file)
124+
reader.Update()
103125

104-
# Load the STL files and add the vectors to the plot
105-
stl_mesh = mesh.Mesh.from_file(plot_file)
106-
axes.add_collection3d(mplot3d.art3d.Poly3DCollection(stl_mesh.vectors))
126+
data = reader.GetOutput()
127+
points = data.GetPoints()
128+
ncells = data.GetNumberOfCells()
107129

108-
# Get the limits of the axis and center the geometry
109-
max_dim = np.array([np.max(stl_mesh.vectors[:,:,0]), \
110-
np.max(stl_mesh.vectors[:,:,1]), \
111-
np.max(stl_mesh.vectors[:,:,2])])
112-
min_dim = np.array([np.min(stl_mesh.vectors[:,:,0]), \
113-
np.min(stl_mesh.vectors[:,:,1]), \
114-
np.min(stl_mesh.vectors[:,:,2])])
130+
# for each cell it contains the indeces of the points that define the cell
131+
figure = plt.figure()
132+
axes = a3.Axes3D(figure)
133+
vtx = np.zeros((ncells, 3, 3))
134+
for i in range(0, ncells):
135+
for j in range(0, 3):
136+
cell = data.GetCell(i).GetPointId(j)
137+
vtx[i][j][0], vtx[i][j][1], vtx[i][j][2] = points.GetPoint(
138+
int(cell)
139+
)
140+
tri = a3.art3d.Poly3DCollection([vtx[i]])
141+
tri.set_color('b')
142+
tri.set_edgecolor('k')
143+
axes.add_collection3d(tri)
144+
145+
## Get the limits of the axis and center the geometry
146+
max_dim = np.array([np.max(vtx[:,:,0]), \
147+
np.max(vtx[:,:,1]), \
148+
np.max(vtx[:,:,2])])
149+
min_dim = np.array([np.min(vtx[:,:,0]), \
150+
np.min(vtx[:,:,1]), \
151+
np.min(vtx[:,:,2])])
115152

116153
max_lenght = np.max(max_dim - min_dim)
117154
axes.set_xlim(
@@ -129,8 +166,56 @@ def plot(self, plot_file=None, save_fig=False):
129166

130167
# Show the plot to the screen
131168
if not save_fig:
132-
pyplot.show()
169+
plt.show()
133170
else:
134171
figure.savefig(plot_file.split('.')[0] + '.png')
135172

136173
return figure
174+
175+
def show(self, show_file=None):
176+
"""
177+
Method to show a vtk file. If `show_file` is not given it shows
178+
`self.infile`.
179+
180+
:param string show_file: the vtk filename you want to show.
181+
"""
182+
if show_file is None:
183+
show_file = self.infile
184+
else:
185+
self._check_filename_type(show_file)
186+
187+
# Read the source file.
188+
reader = vtk.vtkSTLReader()
189+
reader.SetFileName(show_file)
190+
reader.Update() # Needed because of GetScalarRange
191+
output = reader.GetOutput()
192+
scalar_range = output.GetScalarRange()
193+
194+
# Create the mapper that corresponds the objects of the vtk file
195+
# into graphics elements
196+
mapper = vtk.vtkDataSetMapper()
197+
if vtk.VTK_MAJOR_VERSION <= 5:
198+
mapper.SetInput(output)
199+
else:
200+
mapper.SetInputData(output)
201+
mapper.SetScalarRange(scalar_range)
202+
203+
# Create the Actor
204+
actor = vtk.vtkActor()
205+
actor.SetMapper(mapper)
206+
207+
# Create the Renderer
208+
renderer = vtk.vtkRenderer()
209+
renderer.AddActor(actor)
210+
# Set background color (white is 1, 1, 1)
211+
renderer.SetBackground(20, 20, 20)
212+
213+
# Create the RendererWindow
214+
renderer_window = vtk.vtkRenderWindow()
215+
renderer_window.AddRenderer(renderer)
216+
217+
# Create the RendererWindowInteractor and display the vtk_file
218+
interactor = vtk.vtkRenderWindowInteractor()
219+
interactor.SetRenderWindow(renderer_window)
220+
interactor.Initialize()
221+
interactor.Start()

0 commit comments

Comments
 (0)