Coder Social home page Coder Social logo

wolph / numpy-stl Goto Github PK

View Code? Open in Web Editor NEW
607.0 33.0 103.0 1.25 MB

Simple library to make working with STL files (and 3D objects in general) fast and easy.

Home Page: http://numpy-stl.readthedocs.org/

License: BSD 3-Clause "New" or "Revised" License

Python 92.75% Batchfile 0.88% Cython 6.37%
python python3 python2 numpy stl 3d fast high-performance

numpy-stl's Introduction

numpy-stl

numpy-stl test status numpy-stl test status numpy-stl Pypi version numpy-stl code coverage

Simple library to make working with STL files (and 3D objects in general) fast and easy.

Due to all operations heavily relying on numpy this is one of the fastest STL editing libraries for Python available.

Security contact information

To report a security vulnerability, please use the Tidelift security contact. Tidelift will coordinate the fix and disclosure.

Issues

If you encounter any issues, make sure you report them here. Be sure to search for existing issues however. Many issues have been covered before. While this project uses numpy as it's main dependency, it is not in any way affiliated to the numpy project or the NumFocus organisation.

Links

Requirements for installing:

Installation:

pip install numpy-stl

Initial usage:

After installing the package, you should be able to run the following commands similar to how you can run pip.

$ stl2bin your_ascii_stl_file.stl new_binary_stl_file.stl
$ stl2ascii your_binary_stl_file.stl new_ascii_stl_file.stl
$ stl your_ascii_stl_file.stl new_binary_stl_file.stl

Contributing:

Contributions are always welcome. Please view the guidelines to get started: https://github.com/WoLpH/numpy-stl/blob/develop/CONTRIBUTING.rst

Quickstart

import numpy
from stl import mesh

# Using an existing stl file:
your_mesh = mesh.Mesh.from_file('some_file.stl')

# Or creating a new mesh (make sure not to overwrite the `mesh` import by
# naming it `mesh`):
VERTICE_COUNT = 100
data = numpy.zeros(VERTICE_COUNT, dtype=mesh.Mesh.dtype)
your_mesh = mesh.Mesh(data, remove_empty_areas=False)

# The mesh normals (calculated automatically)
your_mesh.normals
# The mesh vectors
your_mesh.v0, your_mesh.v1, your_mesh.v2
# Accessing individual points (concatenation of v0, v1 and v2 in triplets)
assert (your_mesh.points[0][0:3] == your_mesh.v0[0]).all()
assert (your_mesh.points[0][3:6] == your_mesh.v1[0]).all()
assert (your_mesh.points[0][6:9] == your_mesh.v2[0]).all()
assert (your_mesh.points[1][0:3] == your_mesh.v0[1]).all()

your_mesh.save('new_stl_file.stl')

Plotting using matplotlib is equally easy:

from stl import mesh
from mpl_toolkits import mplot3d
from matplotlib import pyplot

# Create a new plot
figure = pyplot.figure()
axes = figure.add_subplot(projection='3d')

# Load the STL files and add the vectors to the plot
your_mesh = mesh.Mesh.from_file('tests/stl_binary/HalfDonut.stl')
axes.add_collection3d(mplot3d.art3d.Poly3DCollection(your_mesh.vectors))

# Auto scale to the mesh size
scale = your_mesh.points.flatten()
axes.auto_scale_xyz(scale, scale, scale)

# Show the plot to the screen
pyplot.show()

Experimental support for reading 3MF files

import pathlib
import stl

path = pathlib.Path('tests/3mf/Moon.3mf')

# Load the 3MF file
for m in stl.Mesh.from_3mf_file(path):
    # Do something with the mesh
    print('mesh', m)

Note that this is still experimental and may not work for all 3MF files. Additionally it only allows reading 3mf files, not writing them.

Modifying Mesh objects

from stl import mesh
import math
import numpy

# Create 3 faces of a cube
data = numpy.zeros(6, dtype=mesh.Mesh.dtype)

# Top of the cube
data['vectors'][0] = numpy.array([[0, 1, 1],
                                  [1, 0, 1],
                                  [0, 0, 1]])
data['vectors'][1] = numpy.array([[1, 0, 1],
                                  [0, 1, 1],
                                  [1, 1, 1]])
# Front face
data['vectors'][2] = numpy.array([[1, 0, 0],
                                  [1, 0, 1],
                                  [1, 1, 0]])
data['vectors'][3] = numpy.array([[1, 1, 1],
                                  [1, 0, 1],
                                  [1, 1, 0]])
# Left face
data['vectors'][4] = numpy.array([[0, 0, 0],
                                  [1, 0, 0],
                                  [1, 0, 1]])
data['vectors'][5] = numpy.array([[0, 0, 0],
                                  [0, 0, 1],
                                  [1, 0, 1]])

# Since the cube faces are from 0 to 1 we can move it to the middle by
# substracting .5
data['vectors'] -= .5

# Generate 4 different meshes so we can rotate them later
meshes = [mesh.Mesh(data.copy()) for _ in range(4)]

# Rotate 90 degrees over the Y axis
meshes[0].rotate([0.0, 0.5, 0.0], math.radians(90))

# Translate 2 points over the X axis
meshes[1].x += 2

# Rotate 90 degrees over the X axis
meshes[2].rotate([0.5, 0.0, 0.0], math.radians(90))
# Translate 2 points over the X and Y points
meshes[2].x += 2
meshes[2].y += 2

# Rotate 90 degrees over the X and Y axis
meshes[3].rotate([0.5, 0.0, 0.0], math.radians(90))
meshes[3].rotate([0.0, 0.5, 0.0], math.radians(90))
# Translate 2 points over the Y axis
meshes[3].y += 2


# Optionally render the rotated cube faces
from matplotlib import pyplot
from mpl_toolkits import mplot3d

# Create a new plot
figure = pyplot.figure()
axes = figure.add_subplot(projection='3d')

# Render the cube faces
for m in meshes:
    axes.add_collection3d(mplot3d.art3d.Poly3DCollection(m.vectors))

# Auto scale to the mesh size
scale = numpy.concatenate([m.points for m in meshes]).flatten()
axes.auto_scale_xyz(scale, scale, scale)

# Show the plot to the screen
pyplot.show()

Extending Mesh objects

from stl import mesh
import math
import numpy

# Create 3 faces of a cube
data = numpy.zeros(6, dtype=mesh.Mesh.dtype)

# Top of the cube
data['vectors'][0] = numpy.array([[0, 1, 1],
                                  [1, 0, 1],
                                  [0, 0, 1]])
data['vectors'][1] = numpy.array([[1, 0, 1],
                                  [0, 1, 1],
                                  [1, 1, 1]])
# Front face
data['vectors'][2] = numpy.array([[1, 0, 0],
                                  [1, 0, 1],
                                  [1, 1, 0]])
data['vectors'][3] = numpy.array([[1, 1, 1],
                                  [1, 0, 1],
                                  [1, 1, 0]])
# Left face
data['vectors'][4] = numpy.array([[0, 0, 0],
                                  [1, 0, 0],
                                  [1, 0, 1]])
data['vectors'][5] = numpy.array([[0, 0, 0],
                                  [0, 0, 1],
                                  [1, 0, 1]])

# Since the cube faces are from 0 to 1 we can move it to the middle by
# substracting .5
data['vectors'] -= .5

cube_back = mesh.Mesh(data.copy())
cube_front = mesh.Mesh(data.copy())

# Rotate 90 degrees over the X axis followed by the Y axis followed by the
# X axis
cube_back.rotate([0.5, 0.0, 0.0], math.radians(90))
cube_back.rotate([0.0, 0.5, 0.0], math.radians(90))
cube_back.rotate([0.5, 0.0, 0.0], math.radians(90))

cube = mesh.Mesh(numpy.concatenate([
    cube_back.data.copy(),
    cube_front.data.copy(),
]))

# Optionally render the rotated cube faces
from matplotlib import pyplot
from mpl_toolkits import mplot3d

# Create a new plot
figure = pyplot.figure()
axes = figure.add_subplot(projection='3d')

# Render the cube
axes.add_collection3d(mplot3d.art3d.Poly3DCollection(cube.vectors))

# Auto scale to the mesh size
scale = cube_back.points.flatten()
axes.auto_scale_xyz(scale, scale, scale)

# Show the plot to the screen
pyplot.show()

Creating Mesh objects from a list of vertices and faces

import numpy as np
from stl import mesh

# Define the 8 vertices of the cube
vertices = np.array([\
    [-1, -1, -1],
    [+1, -1, -1],
    [+1, +1, -1],
    [-1, +1, -1],
    [-1, -1, +1],
    [+1, -1, +1],
    [+1, +1, +1],
    [-1, +1, +1]])
# Define the 12 triangles composing the cube
faces = np.array([\
    [0,3,1],
    [1,3,2],
    [0,4,7],
    [0,7,3],
    [4,5,6],
    [4,6,7],
    [5,1,2],
    [5,2,6],
    [2,3,6],
    [3,7,6],
    [0,1,5],
    [0,5,4]])

# Create the mesh
cube = mesh.Mesh(np.zeros(faces.shape[0], dtype=mesh.Mesh.dtype))
for i, f in enumerate(faces):
    for j in range(3):
        cube.vectors[i][j] = vertices[f[j],:]

# Write the mesh to file "cube.stl"
cube.save('cube.stl')

Evaluating Mesh properties (Volume, Center of gravity, Inertia)

import numpy as np
from stl import mesh

# Using an existing closed stl file:
your_mesh = mesh.Mesh.from_file('some_file.stl')

volume, cog, inertia = your_mesh.get_mass_properties()
print("Volume                                  = {0}".format(volume))
print("Position of the center of gravity (COG) = {0}".format(cog))
print("Inertia matrix at expressed at the COG  = {0}".format(inertia[0,:]))
print("                                          {0}".format(inertia[1,:]))
print("                                          {0}".format(inertia[2,:]))

Combining multiple STL files

import math
import stl
from stl import mesh
import numpy


# find the max dimensions, so we can know the bounding box, getting the height,
# width, length (because these are the step size)...
def find_mins_maxs(obj):
    minx = obj.x.min()
    maxx = obj.x.max()
    miny = obj.y.min()
    maxy = obj.y.max()
    minz = obj.z.min()
    maxz = obj.z.max()
    return minx, maxx, miny, maxy, minz, maxz


def translate(_solid, step, padding, multiplier, axis):
    if 'x' == axis:
        items = 0, 3, 6
    elif 'y' == axis:
        items = 1, 4, 7
    elif 'z' == axis:
        items = 2, 5, 8
    else:
        raise RuntimeError('Unknown axis %r, expected x, y or z' % axis)

    # _solid.points.shape == [:, ((x, y, z), (x, y, z), (x, y, z))]
    _solid.points[:, items] += (step * multiplier) + (padding * multiplier)


def copy_obj(obj, dims, num_rows, num_cols, num_layers):
    w, l, h = dims
    copies = []
    for layer in range(num_layers):
        for row in range(num_rows):
            for col in range(num_cols):
                # skip the position where original being copied is
                if row == 0 and col == 0 and layer == 0:
                    continue
                _copy = mesh.Mesh(obj.data.copy())
                # pad the space between objects by 10% of the dimension being
                # translated
                if col != 0:
                    translate(_copy, w, w / 10., col, 'x')
                if row != 0:
                    translate(_copy, l, l / 10., row, 'y')
                if layer != 0:
                    translate(_copy, h, h / 10., layer, 'z')
                copies.append(_copy)
    return copies

# Using an existing stl file:
main_body = mesh.Mesh.from_file('ball_and_socket_simplified_-_main_body.stl')

# rotate along Y
main_body.rotate([0.0, 0.5, 0.0], math.radians(90))

minx, maxx, miny, maxy, minz, maxz = find_mins_maxs(main_body)
w1 = maxx - minx
l1 = maxy - miny
h1 = maxz - minz
copies = copy_obj(main_body, (w1, l1, h1), 2, 2, 1)

# I wanted to add another related STL to the final STL
twist_lock = mesh.Mesh.from_file('ball_and_socket_simplified_-_twist_lock.stl')
minx, maxx, miny, maxy, minz, maxz = find_mins_maxs(twist_lock)
w2 = maxx - minx
l2 = maxy - miny
h2 = maxz - minz
translate(twist_lock, w1, w1 / 10., 3, 'x')
copies2 = copy_obj(twist_lock, (w2, l2, h2), 2, 2, 1)
combined = mesh.Mesh(numpy.concatenate([main_body.data, twist_lock.data] +
                                    [copy.data for copy in copies] +
                                    [copy.data for copy in copies2]))

combined.save('combined.stl', mode=stl.Mode.ASCII)  # save as ASCII

Known limitations

  • When speedups are enabled the STL name is automatically converted to lowercase.

numpy-stl's People

Contributors

bwoodsend avatar csachs avatar gjacquenot avatar hakostra avatar hongbo-miao avatar hroncok avatar hyperkang avatar james-archer avatar jb-leger avatar jorgectf avatar karl-nilsson avatar loicgasser avatar mamrehn avatar nicholaswon47 avatar nxsofsys avatar oscargus avatar pyup-bot avatar wolph avatar xorgon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

numpy-stl's Issues

stl.py not python-3.4 compatible

I've installed numpy-stl with pip3 for python-3.4 and pip for python-2.7

The following code works with python 2.7:
from stl import mesh

But with python-3.4 I get the following error:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.4/dist-packages/stl/mesh.py", line 1, in <module> from . import stl
File "/usr/local/lib/python3.4/dist-packages/stl/stl.py", line 49   
 except RuntimeError, (recoverable, e):
                    ^
SyntaxError: invalid syntax

Editing line 49 to except RuntimeError (recoverable, e): removes the error and put another one:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.4/dist-packages/stl/mesh.py", line 1, in <module>
    from . import stl
  File "/usr/local/lib/python3.4/dist-packages/stl/stl.py", line 155
    except AssertionError, e:
                         ^
SyntaxError: invalid syntax

write matplotlib surfaceplot to stl file

Would it be possible to write a matplotlib trisurf plot to an stl file? (I was hoping to print my graphs on a 3d printer)

I've been messing around with the Poly3dCollection object that is returned by the trisurf command, but I do not understand how you can get the actual vectors out of it. I also do not completely understand what sort of data the Mesh constructor expects.

# %load http://matplotlib.org/mpl_examples/mplot3d/trisurf3d_demo2.py
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.tri as mtri

# u, v are parameterisation variables
u = (np.linspace(0, 2.0 * np.pi, endpoint=True, num=50) * np.ones((10, 1))).flatten()
v = np.repeat(np.linspace(-0.5, 0.5, endpoint=True, num=10), repeats=50).flatten()

# This is the Mobius mapping, taking a u, v pair and returning an x, y, z
# triple
x = (1 + 0.5 * v * np.cos(u / 2.0)) * np.cos(u)
y = (1 + 0.5 * v * np.cos(u / 2.0)) * np.sin(u)
z = 0.5 * v * np.sin(u / 2.0)

# Triangulate parameter space to determine the triangles
tri = mtri.Triangulation(u, v)

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')

# The triangles in parameter space determine which x, y, z points are
# connected by an edge
mobius_polycollection = ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap=plt.cm.Spectral)


# now convert mobius_polycollection to mesh
import stl

mobius_mesh = #I have no idea...

mobius_mesh.save('mobius.stl')

Translation of Centre of Gravity to global origin

When center of gravity is calculated for a stl file, and the mesh is translated such that
my_mesh.translate(-1*cog), i.e entire mesh is translated such that its cog coincides with the origin. But when the cog of the translated mesh is calculated, it does not show origin, but rather a very small value such as ([ -1.02913186e-07, -1.07319586e-07, -2.17118228e-06]). So the mesh is not accurately translating to origin. Please check

Convert to jpg file

Sorry for a silly question.
Is here a way to take something like a snapshot of stl file in jpeg format?
If it is not, may somebody know how to do this stuff?
Thanks.

STL files containing multiple solids

We're seeing some STL files in the wild that have multiple solids in them. The one we were pointed to is here: https://www.thingiverse.com/thing:1322352

The file in question essentially has the following format:

solid ascii
    ... <lots of facets>
endsolid
solid ascii
    ... <lots of facets>
endsolid

numpy-stl loads only the first of the solids in the file.

The only STL file format specification I could find doesn't mention that this is allowed: http://www.fabbers.com/tech/STL_Format
However as these files are going around on the internet, you could consider loading them. I would expect them to load as if all facets are in the same solid.

Help with writting binary stl file to opened zip file

Hi,
I really like your library and I'm using it in one project for reading and writting stl files from/to zip file.

But I found only way for writting/reading ascii type of stl not for binary:

#ascii type
with ZipFile(filename, 'w', ZIP_DEFLATED) as openedZipfile:
    fileLike = StringIO()
    mesh._write_ascii(fh=fileLike, name=model.filename)
    openedZipfile.writestr(model.filename, fileLike.getvalue())
    fileLike.close()

Do you have any idea how to write a binary type?

Many thanks

Facet Subdivision

Hello, I am looking at numpy-stl to use in to import stl files for a python based geometrical optics model and I need a way to set a maximum facet area. I had assumed a median subdivision algorithm was the best way to go but do you have any advice on the most efficient way to implement it? I'm still working out the structure for adding and removing facets.

Rotate and rotation-matrix

Hello,

I've read the documentation and the examples about the rotate method.
But I didn't understand if when you give a vector for the axis, your angle teta and a point it will do a rotation of teta around the axis which passes by the point and directed by the vector .

Or do I need to use rotation_matrix with my vector for the axis after translating all my points ?

Thank you for your help

Feature request: unify duplicated vertices

This is really just a space optimization, but when you create an STL file, it does not unify the vertices so you have a lot of duplicated data. I have to open all the files in meshlab to compress the stl file like so. It would be a handy function to implement in this library especially if you are generating a ton of STL files.

Translation by -COG doesn't make origo new COG

After translating a mesh with -COG one would expect the new COG to be the coordinate origin (0, 0, 0) (modulo small float errors), but when running the code found in the gist with gear-complex.stl (also in the gist), the new COG is clearly off-origin. The z component changes from +1.6 to -12 and thus gets further away from origo.

https://gist.github.com/md2perpe/80b097e58ddfd5f6cb48c014875ab565

System info:

$ uname -srvmpio
Linux 3.16.0-4-amd64 #1 SMP Debian 3.16.43-2 (2017-04-30) x86_64 unknown unknown GNU/Linux

mesh.from_file throws Assertion Error if matplotlib figure is open when speedups=True

I run into troubles loading .stl files with stl.Mesh.from_file when there is some instance of matplotlib or Qt running in the same process. That is

import stl
import matplotlib.pyplot as plt

mesh=stl.Mesh.from_file(pathToStlFile)

works great. However, if before initiate a matplotlib figure, that is

plt.figure()
mesh=stl.Mesh.from_file(pathToStlFile)

I get the following Assertion Error:

 File "/usr/local/lib/python2.7/dist-packages/stl/stl.py", line 316, in from_file
    fh, mode=mode, speedups=speedups)
  File "/usr/local/lib/python2.7/dist-packages/stl/stl.py", line 85, in load
    name, data = cls._load_binary(fh, header, check_size=True)
  File "/usr/local/lib/python2.7/dist-packages/stl/stl.py", line 102, in _load_binary
    count, MAX_COUNT)
AssertionError: File too large, got 909389366 triangles which exceeds the maximum of 100000000

Looking through the code in BaseStl and reading through this issue #37, I realized that this is due to the fallback if _load_ascii throws a runtime error such as

 File "/usr/local/lib/python2.7/dist-packages/stl/stl.py", line 212, in _load_ascii
    return _speedups.ascii_read(fh, header)
  File "stl/_speedups.pyx", line 132, in stl._speedups.ascii_read (stl/_speedups.c:2613)
RuntimeError: (0, "Can't read vertex (4:vertex 176.46 176.46 0)")

So it seems that somehow the speedup is not working properly (some Cython clash with Qt/matplotlib?). Turns out that

plt.figure()
mesh=stl.Mesh.from_file(pathToStlFile,speedup=False)

works great. This took me a while to figure out and I thought it might be interesting.

Error in numpy-stl

Hello,

I got an error with your stl.py.
I just installed numpy-stl and got this error:

from stl import stl
Traceback (most recent call last):
File "", line 1, in
File "C:\Python34\lib\site-packages\stl\stl.py", line 58
except RuntimeError, (recoverable, e):
^
SyntaxError: invalid syntax

I'm using Win 8.1 and Python 3.4.2.

I tried to fix the error with deleting the ',' but then another error occurs;

from stl import stl
Traceback (most recent call last):
File "", line 1, in
File "C:\Python34\lib\site-packages\stl\stl.py", line 157
except AssertionError, e:
^

May you fix that error or help me to fix it?
Thanks in advance.

Normals per vertex

It would be nice to (optionally) get the normals for each vertex instead of per triangle. Usually adjacency information is needed for the triangles in that case, but I'm not sure there are any guarantees to the order of the triangles given in STL and therefore numpy-stl.

Documentation unclear on auto-computation of normals

I have a 3D model I want to save from Python, which has lots of holes in the middle. Your module looks like the best way to write out the file. However, after reading this:

http://www.fabbers.com/tech/STL_Format

it looks as though the normal direction is (as I suspected) significant in the output format, in that it must point from solid phase to empty space. Obviously, there are two choices of normal for any set of three coordinates.

You don't seem to have documented a way to initialise from data containing both the facets and their normals (claiming you auto-compute them.) I would very much like to know how your code does this, as it would be non-trivial to do so from the vectors and facets in my system alone.

If there is already a manual way to specify the facet normals when initialising the stl.mesh.Mesh class, I would be grateful if you could explain it here, and perhaps amend the documentation to reflect that.

Thanks!

Ascii stl solid names handling

Hello,

firstly, many thanks for your work and the package!
I would like to read a big ascii stl and would like to modify the part names in it.
The stl has the usual form:
solid part1
facet normal -1.00000E+000 6.25745E-015 1.16257E-014
outer loop
vertex 1.08800E+003 -3.51005E+002 5.06590E+002
vertex 1.08800E+003 -3.51005E+002 5.26148E+002
vertex 1.08800E+003 -3.38068E+002 5.19185E+002
endloop
endfacet
...
endsolid part1
solid part2
...

How can I get the part names (which come after the solid strings) from the mesh.Mesh.from_file(stlfile) object?
I had a quick look at the documentation but could not find an attribute where they are stored or a method that would returned them.
Also, when writing out the ascii stl again can I provide my own part names?

Thanks in advance,
Lazlo

stl to nrrd volumes

Hi

I have mesh in .stl files and I want to convert to .nrrd files to get volume not header information.

How I can proceed? Thanks in advance

Slicing with numpy-stl

Hi,
Is there an option within numpy-stl to slice the 3D arrays in numpy into 2D slices? If not, do you have suggestions on how to do this?
Cheers and happy new year!

How do I?

So far this library looks really cool.
Perhaps I'm missing something really obvious but there are two actions I don't see in the documentation.

How do I create and save a new stl file?

How do I extend or reduce the mesh for an existing mesh object?

After version 1.4.0 something changed in the data representation

I can't really trace it, will spend some more hours on it, but probably something broke when adding support for python 3.

Error message in one of the scripts:
"Invalid value encountered in double_scalars"
When I try to do 2 * x * y/(x ** 2+y ** 2) with x and y scalars.
In other words: x and y are both zero all of a sudden, and the division is invalid.

This worked without a hitch in version 1.4.0. ^^

How to generate STL from loaded numpy array?

Hello!

I'm struggling with a relatively simple issue. I've got several vtkPolyData files and I'd like to transform them into numpy arrays and then save them to npz file. For that purpose I decided to use numpy-stl module in such a way:

  1. Convert vtkPolyData to STL files
  2. Load STL files via numpy-stl
  3. Save numpy arrays to npz file
  4. Load npz file and get all arrays from them
  5. Recreate STL files and then vtkPolyData files.

However, I do not know how to recreate the STL files from the loaded npz file. I would be grateful if you could help me. Here is a sample code for 2 stls in which I'm stuck (I don't know what to do next):

mesh_1 = mesh.Mesh.from_file("surface_1.stl")
mesh_2 = mesh.Mesh.from_file("surface_2.stl")
numpy.savez("file_with_arrays.npz",mesh_1=mesh_1,mesh_2 =mesh_2 )
new_mesh1=npzfile['mesh_1']
new_mesh1 = mesh.Mesh(new_mesh1, remove_empty_areas=False)
new_mesh1.normals
new_mesh1.save('new_stl_file1.stl')
new_mesh2=npzfile['mesh_2']
new_mesh2 = mesh.Mesh(new_mesh2, remove_empty_areas=False)
new_mesh2.normals
new_mesh2.save('new_stl_file2.stl')

This complicated way of trying to save vtkPolyData into numpy arrays seems strange, however, I don't know how I can do it without numpy-stl module. If you could propose another way of doing it, I'd be extremely grateful. I tried searching for transforming vtkPolyData into vtkArray, then into numpy array and then vice versa, but I didn't find anything helpful. Thank you in advance for your help!

AttributeError: type object 'Mesh' has no attribute 'logger'

When reading an STL file without a name or space after solid, numpy-stl wants to warn me

        if not line.startswith(b('solid ')) and line.startswith(b('solid')):
            cls.warning('ASCII STL files should start with solid <space>. '
                        'The application that produced this STL file may be '
                        'faulty, please report this error. The erroneous '
                        'line: %r', line)

However, this fails with

Traceback (most recent call last):
  File "test.py", line 20, in <module>
    stlmesh = mesh.Mesh.from_file(args.input_file)
  File "/usr/local/lib/python3.5/dist-packages/stl/stl.py", line 316, in from_file
    fh, mode=mode, speedups=speedups)
  File "/usr/local/lib/python3.5/dist-packages/stl/stl.py", line 69, in load
    fh, header, speedups=speedups)
  File "/usr/local/lib/python3.5/dist-packages/stl/stl.py", line 215, in _load_ascii
    name = next(iterator)
  File "/usr/local/lib/python3.5/dist-packages/stl/stl.py", line 178, in _ascii_reader
    'line: %r', line)
  File "/usr/local/lib/python3.5/dist-packages/python_utils/logger.py", line 46, in warning
    cls.logger.warning(msg, *args, **kwargs)
AttributeError: type object 'Mesh' has no attribute 'logger'

Note that in my personal opinion it would be highly desirable to configure the level of strictness when reading STLs. The current implementation adheres to the standard which is good. However, many defective CAD tools fail to obey the specifications and it would be helpful if numpy-stl could adapt to these cases as well.

Installation Details:

  • Python 3.5.2 (default, Sep 10 2016, 08:21:44) [GCC 5.4.0 20160609] on linux
  • OS Ubuntu 16.04
  • numpy-stl==2.0.0
  • python-utils==2.0.0

pip install numpy-stl fails with python v3.4+

The enum34 requirement in setup.py's install_requires list induces an error during installation with pip for python versions 3.4 and higher.

More info about enum34: enum34-incompatible-with-python-35.
Is there a way to import enum34 depending on the python version used instead?

Error log:

$ pip install numpy-stl
Collecting numpy-stl
  Using cached numpy-stl-1.7.0.tar.gz
Requirement already satisfied (use --upgrade to upgrade): numpy in ./miniconda3/envs/testenv/lib/python3.5/site-packages (from numpy-stl)
Collecting nine (from numpy-stl)
  Using cached nine-0.3.4.tar.gz
Collecting enum34 (from numpy-stl)
  Using cached enum34-1.0.4.tar.gz
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "/tmp/pip-build-27fj2ir5/enum34/enum/__init__.py", line 371, in __getattr__
        return cls._member_map_[name]
    KeyError: '_convert'

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      [...]
      File "/home/username/miniconda3/envs/testenv/lib/python3.5/socket.py", line 73, in <module>
        IntEnum._convert(
      File "/tmp/pip-build-27fj2ir5/enum34/enum/__init__.py", line 373, in __getattr__
        raise AttributeError(name)
    AttributeError: _convert

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-27fj2ir5/enum34

Detaching vectors and data['vectors']

Consider a mesh object defined as MO = mesh.Mesh.from_file(TT)

Due to the way Python works, if I use inplace operations on MO.vectors, then MO.vectors and MO.data are both updated. However, if I assign something to MO.vectors, then the MO.data isn't updated and the object basically has two different meshes at the same time.

The problem with this behavior is that changing MO.vectors doesn't consistently work (see example below). This is concerning because some operations work on MO.vectors assuming that no detachment occurred (Rotation, min, max, etc). Even in one of the examples the construction of the mesh is done by operating on MO.vectors.

I would suggest not having a MO.data at all but maybe a warning before saving if MO.data['vectors'] is different from MO.vectors would already be very helpful in debugging.

Best regards,
Miguel

from __future__ import division
import numpy as np
import stl
from stl import mesh

TT = "TwoTriangles.stl"
TTo="TwoTriangles_out.stl"

MO = mesh.Mesh.from_file(TT)

MO.vectors *=0.5 #Does not detach
print "Not-Detached difference   = " + str(np.linalg.norm(np.array(MO.vectors-MO.data['vectors'])))
MO.vectors *=2.0

for i in range(2):
    for j in range(3):
        MO.vectors[i][j] = MO.vectors[i][j]*0.5
print "Not-Detached difference 2 = " + str(np.linalg.norm(np.array(MO.vectors-MO.data['vectors'])))
MO.vectors *=2.0

MO.vectors = MO.vectors*0.5 #Detaches
#MO.data['vectors']=MO.vectors
print "Detached difference       = " + str(np.linalg.norm(np.array(MO.vectors-MO.data['vectors'])))
		
MO.save(TTo,mode=stl.Mode.ASCII)
MR = mesh.Mesh.from_file(TTo)
print "Loaded File difference    = " + str(np.linalg.norm(np.array(MO.vectors-MR.vectors)))

The TwoTriangles.stl file looks like this:

solid TwoTriangles.stl
facet normal 0.000000 0.000000 1.000000
  outer loop
    vertex -1.000000 1.000000 0.000000
    vertex -2.000000 0.000000 0.000000
    vertex -1.000000 0.000000 0.000000
  endloop
endfacet
facet normal 0.000000 0.000000 1.000000
  outer loop
    vertex 0.000000 1.000000 0.000000
    vertex -1.000000 1.000000 0.000000
    vertex -1.000000 0.000000 0.000000
  endloop
endfacet
endsolid TwoTriangles.stl

The result:

Not-Detached difference   = 0.0
Not-Detached difference 2 = 0.0
Detached difference       = 1.65831
Loaded File difference    = 1.65831

Cannot import mesh

I'm a novice and this is probably user error, but when I run my program I receive error: cannot import mesh.

Any idea what this may be about? Please be specific in your response, as I am a beginner in Python.

Thanks so much for your help.

Tests failing on big endian

I'm trying to package numpy-stl for Fedora.
However, currently the tests fail on ppc64 which is Big Endian.

Full build log, just the tests:

+ /usr/bin/python3 setup.py pytest
running pytest
running egg_info
writing numpy_stl.egg-info/PKG-INFO
writing dependency_links to numpy_stl.egg-info/dependency_links.txt
writing entry points to numpy_stl.egg-info/entry_points.txt
writing requirements to numpy_stl.egg-info/requires.txt
writing top-level names to numpy_stl.egg-info/top_level.txt
reading manifest file 'numpy_stl.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
warning: manifest_maker: MANIFEST.in, line 1: unknown action 'include-recursive'
warning: no previously-included files matching '*.py[co]' found anywhere in distribution
no previously-included directories found matching 'docs/_build'
writing manifest file 'numpy_stl.egg-info/SOURCES.txt'
running build_ext
copying build/lib.linux-ppc64-3.6/stl/_speedups.cpython-36m-ppc64-linux-gnu.so -> stl
============================= test session starts ==============================
platform linux -- Python 3.6.0, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
rootdir: /builddir/build/BUILD/numpy-stl-2.1.0, inifile: 
collected 58 items
tests/test_ascii.py ..
tests/test_commandline.py FF..F...
tests/test_convert.py ......
tests/test_mesh.py ..............
tests/test_meshProperties.py FFFFFF
tests/test_multiple.py ........
tests/test_rotate.py ..............
=================================== FAILURES ===================================
_______________________________ test_main[False] _______________________________
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
mode = <Mode.AUTOMATIC: 0>, speedups = False
    @classmethod
    def load(cls, fh, mode=AUTOMATIC, speedups=True):
        '''Load Mesh from STL file
    
            Automatically detects binary versus ascii STL files.
    
            :param file fh: The file handle to open
            :param int mode: Automatically detect the filetype or force binary
            '''
        header = fh.read(HEADER_SIZE).lower()
        if not header:
            return
    
        if isinstance(header, str):  # pragma: no branch
            header = b(header)
    
        name = ''
    
        if mode in (AUTOMATIC, ASCII) and header.startswith(b('solid')):
            try:
                name, data = cls._load_ascii(
>                   fh, header, speedups=speedups)
stl/stl.py:71: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
header = b'solid test.stl    2014-10-16 15:37:40.542327 halfdonut.stl                      '
speedups = False
    @classmethod
    def _load_ascii(cls, fh, header, speedups=True):
        if _speedups and speedups:
            return _speedups.ascii_read(fh, header)
        else:
            iterator = cls._ascii_reader(fh, header)
            name = next(iterator)
>           return name, numpy.fromiter(iterator, dtype=cls.dtype)
stl/stl.py:218: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
header = b'solid test.stl    2014-10-16 15:37:40.542327 halfdonut.stl                       \x01\x00\x00;\xa1n\xbc\xe6\xf5\x95=...@(\xefC>;\xc7p?\x00\x00\x83\xe1\x91=T\xf7e=H\xf2B\xbd7q6@(\xefC>;\xc7p?JA\x1d@\xe6\x04\xb5>\t\xfb\n?\x85\x080@\xe6\x04'
    @classmethod
    def _ascii_reader(cls, fh, header):
        if b'\n' in header:
            recoverable = [True]
        else:
            recoverable = [False]
            header += b(fh.read(BUFFER_SIZE))
    
        lines = b(header).split(b('\n'))
    
        def get(prefix=''):
            prefix = b(prefix)
    
            if lines:
                line = lines.pop(0)
            else:
                raise RuntimeError(recoverable[0], 'Unable to find more lines')
            if not lines:
                recoverable[0] = False
    
                # Read more lines and make sure we prepend any old data
                lines[:] = b(fh.read(BUFFER_SIZE)).split(b('\n'))
                line += lines.pop(0)
    
            line = line.lower().strip()
            if line == b(''):
                return get(prefix)
    
            if prefix:
                if line.startswith(prefix):
                    values = line.replace(prefix, b(''), 1).strip().split()
                elif line.startswith(b('endsolid')):
                    # go back to the beginning of new solid part
                    size_unprocessedlines = sum(len(l) + 1 for l in lines) - 1
                    if size_unprocessedlines > 0:
                        position = fh.tell()
                        fh.seek(position - size_unprocessedlines)
                    raise StopIteration()
                else:
                    raise RuntimeError(recoverable[0],
                                       '%r should start with %r' % (line,
                                                                    prefix))
    
                if len(values) == 3:
                    return [float(v) for v in values]
                else:  # pragma: no cover
                    raise RuntimeError(recoverable[0],
                                       'Incorrect value %r' % line)
            else:
                return b(line)
    
        line = get()
        if not line.startswith(b('solid ')) and line.startswith(b('solid')):
            cls.warning('ASCII STL files should start with solid <space>. '
                        'The application that produced this STL file may be '
                        'faulty, please report this error. The erroneous '
                        'line: %r', line)
    
        if not lines:
            raise RuntimeError(recoverable[0],
                               'No lines found, impossible to read')
    
        # Yield the name
        yield line[5:].strip()
    
        while True:
            # Read from the header lines first, until that point we can recover
            # and go to the binary option. After that we cannot due to
            # unseekable files such as sys.stdin
            #
            # Numpy doesn't support any non-file types so wrapping with a
            # buffer and/or StringIO does not work.
            try:
>               normals = get('facet normal')
stl/stl.py:197: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
prefix = b'facet normal'
    def get(prefix=''):
        prefix = b(prefix)
    
        if lines:
            line = lines.pop(0)
        else:
            raise RuntimeError(recoverable[0], 'Unable to find more lines')
        if not lines:
            recoverable[0] = False
    
            # Read more lines and make sure we prepend any old data
            lines[:] = b(fh.read(BUFFER_SIZE)).split(b('\n'))
            line += lines.pop(0)
    
        line = line.lower().strip()
        if line == b(''):
            return get(prefix)
    
        if prefix:
            if line.startswith(prefix):
                values = line.replace(prefix, b(''), 1).strip().split()
            elif line.startswith(b('endsolid')):
                # go back to the beginning of new solid part
                size_unprocessedlines = sum(len(l) + 1 for l in lines) - 1
                if size_unprocessedlines > 0:
                    position = fh.tell()
                    fh.seek(position - size_unprocessedlines)
                raise StopIteration()
            else:
                raise RuntimeError(recoverable[0],
                                   '%r should start with %r' % (line,
>                                                               prefix))
E               RuntimeError: (False, "b'!\\xbc\\xafw\\x7f>\\xe6\\x04\\xb5>\\x88e{?l\\x08\\x9e>f\\x83\\xec>\\x00\\x00\\xc0?\\xd1v\\xcc>f\\x83\\xec>\\xbe\\xa4\\x85?\\x00\\x00\\xb1\\xf5e\\xbd\\xed\\x13\\xac=\\x9f\\xf76\\xbc\\xafw\\x7f>\\xe6\\x04\\xb5>\\x88e{?4\\xf6\\x15>\\xe6\\x04\\xb5>\\x00\\x00\\xc0?l\\x08\\x9e>f\\x83\\xec>\\x00\\x00\\xc0?\\x00\\x00\\xfamj\\xbc[\\xf6\\x95=\\x9c,\\x07\\xbc\\x15x(?f\\x83\\xec>\\x15x(?\\x9a|\\x13?\\x00\\x00\\x00?w\\x04\\x8f?\\t\\xfbj?\\x00\\x00\\x00?\\t\\xfbj?\\x00\\x00c\\x02q\\xbc0\\xa8\\xb2=\\xcf\\t!\\xbc\\x15x(?f\\x83\\xec>\\x15x(?\\xd1v\\xcc>f\\x83\\xec>\\xbe\\xa4\\x85?\\x9a|\\x13?\\x00\\x00\\x00?w\\x04\\x8f?\\x00\\x00\\xfa\\x97#<\\xfa\\x88r=\\x85\\x9e\\xda;\\xfe\\x9dm?f\\x83\\xec>\\xfe\\x9dm?\\t\\xfbj?\\x00\\x00\\x00?\\t\\xfbj?\\xdc\\xbd@?f\\x83\\xec>\\x9dc\\x98?\\x00\\x00\\xfamj<\\x1f\\xf6\\x95=\\x9c,\\x07<\\xdc\\xbd@?f\\x83\\xec>\\x9dc\\x98?\\t\\xfbj?\\x00\\x00\\x00?\\t\\xfbj?\\x9a|\\x13?\\x00\\x00\\x00?w\\x04\\x8f?\\x00\\x00y8\\xba<\\x8c_$=\\xc0\\xd7x<k}\\x85?\\xe6\\x04\\xb5>k}\\x85?\\xdc\\xbd@?f\\x83\\xec>\\x9dc\\x98?x\\x1bg?\\xe6\\x04\\xb5>\\xdau\\xa0?\\x00\\x00\\xbc\\xf0\\xe8<\\x19\\x9bm=b\\xa5\\x9b<k}\\x85?\\xe6\\x04\\xb5>k}\\x85?\\xfe\\x9dm?f\\x83\\xec>\\xfe\\x9dm?\\xdc\\xbd@?f\\x83\\xec>\\x9dc\\x98?\\x00\\x00' should start with b'facet normal'")
stl/stl.py:165: RuntimeError
During handling of the above exception, another exception occurred:
ascii_file = '/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_ascii/HalfDonut.stl'
binary_file = '/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'
tmpdir = local('/tmp/pytest-of-mockbuild/pytest-0/test_main_False_0')
speedups = False
    def test_main(ascii_file, binary_file, tmpdir, speedups):
        original_argv = sys.argv[:]
        args_pre = ['stl']
        args_post = [str(tmpdir.join('output.stl'))]
    
        if not speedups:
            args_pre.append('-s')
    
        try:
            sys.argv[:] = args_pre + [ascii_file] + args_post
            main.main()
            sys.argv[:] = args_pre + ['-r', ascii_file] + args_post
            main.main()
            sys.argv[:] = args_pre + ['-a', binary_file] + args_post
>           main.main()
tests/test_commandline.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stl/main.py:53: in main
    speedups=not args.disable_speedups)
stl/stl.py:314: in from_file
    fh, mode=mode, speedups=speedups)
stl/stl.py:87: in load
    name, data = cls._load_binary(fh, header, check_size=True)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
header = b'solid test.stl    2014-10-16 15:37:40.542327 halfdonut.stl                      '
check_size = True
    @classmethod
    def _load_binary(cls, fh, header, check_size=False):
        # Read the triangle count
        count_data = fh.read(COUNT_SIZE)
        if len(count_data) != COUNT_SIZE:
            count = 0
        else:
            count, = struct.unpack(s('@i'), b(count_data))
        # raise RuntimeError()
        assert count < MAX_COUNT, ('File too large, got %d triangles which '
                                   'exceeds the maximum of %d') % (
>                                      count, MAX_COUNT)
E       AssertionError: File too large, got 536936448 triangles which exceeds the maximum of 100000000
stl/stl.py:104: AssertionError
_______________________________ test_main[True] ________________________________
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
mode = <Mode.AUTOMATIC: 0>, speedups = True
    @classmethod
    def load(cls, fh, mode=AUTOMATIC, speedups=True):
        '''Load Mesh from STL file
    
            Automatically detects binary versus ascii STL files.
    
            :param file fh: The file handle to open
            :param int mode: Automatically detect the filetype or force binary
            '''
        header = fh.read(HEADER_SIZE).lower()
        if not header:
            return
    
        if isinstance(header, str):  # pragma: no branch
            header = b(header)
    
        name = ''
    
        if mode in (AUTOMATIC, ASCII) and header.startswith(b('solid')):
            try:
                name, data = cls._load_ascii(
>                   fh, header, speedups=speedups)
stl/stl.py:71: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
header = b'solid test.stl    2014-10-16 15:37:40.542327 halfdonut.stl                      '
speedups = True
    @classmethod
    def _load_ascii(cls, fh, header, speedups=True):
        if _speedups and speedups:
>           return _speedups.ascii_read(fh, header)
stl/stl.py:214: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
>   raise RuntimeError(state.recoverable,
E   RuntimeError: (0, "Can't read normals (2:b'!\\xbc\\xafw\\x7f>\\xe6\\x04\\xb5>\\x88e{?l\\x08\\x9e>f\\x83\\xec>')")
stl/_speedups.pyx:120: RuntimeError
During handling of the above exception, another exception occurred:
ascii_file = '/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_ascii/HalfDonut.stl'
binary_file = '/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'
tmpdir = local('/tmp/pytest-of-mockbuild/pytest-0/test_main_True_0')
speedups = True
    def test_main(ascii_file, binary_file, tmpdir, speedups):
        original_argv = sys.argv[:]
        args_pre = ['stl']
        args_post = [str(tmpdir.join('output.stl'))]
    
        if not speedups:
            args_pre.append('-s')
    
        try:
            sys.argv[:] = args_pre + [ascii_file] + args_post
            main.main()
            sys.argv[:] = args_pre + ['-r', ascii_file] + args_post
            main.main()
            sys.argv[:] = args_pre + ['-a', binary_file] + args_post
>           main.main()
tests/test_commandline.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stl/main.py:53: in main
    speedups=not args.disable_speedups)
stl/stl.py:314: in from_file
    fh, mode=mode, speedups=speedups)
stl/stl.py:87: in load
    name, data = cls._load_binary(fh, header, check_size=True)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
header = b'solid test.stl    2014-10-16 15:37:40.542327 halfdonut.stl                      '
check_size = True
    @classmethod
    def _load_binary(cls, fh, header, check_size=False):
        # Read the triangle count
        count_data = fh.read(COUNT_SIZE)
        if len(count_data) != COUNT_SIZE:
            count = 0
        else:
            count, = struct.unpack(s('@i'), b(count_data))
        # raise RuntimeError()
        assert count < MAX_COUNT, ('File too large, got %d triangles which '
                                   'exceeds the maximum of %d') % (
>                                      count, MAX_COUNT)
E       AssertionError: File too large, got 536936448 triangles which exceeds the maximum of 100000000
stl/stl.py:104: AssertionError
______________________________ test_ascii[False] _______________________________
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
mode = <Mode.AUTOMATIC: 0>, speedups = False
    @classmethod
    def load(cls, fh, mode=AUTOMATIC, speedups=True):
        '''Load Mesh from STL file
    
            Automatically detects binary versus ascii STL files.
    
            :param file fh: The file handle to open
            :param int mode: Automatically detect the filetype or force binary
            '''
        header = fh.read(HEADER_SIZE).lower()
        if not header:
            return
    
        if isinstance(header, str):  # pragma: no branch
            header = b(header)
    
        name = ''
    
        if mode in (AUTOMATIC, ASCII) and header.startswith(b('solid')):
            try:
                name, data = cls._load_ascii(
>                   fh, header, speedups=speedups)
stl/stl.py:71: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
header = b'solid test.stl    2014-10-16 15:37:40.542327 halfdonut.stl                      '
speedups = False
    @classmethod
    def _load_ascii(cls, fh, header, speedups=True):
        if _speedups and speedups:
            return _speedups.ascii_read(fh, header)
        else:
            iterator = cls._ascii_reader(fh, header)
            name = next(iterator)
>           return name, numpy.fromiter(iterator, dtype=cls.dtype)
stl/stl.py:218: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
header = b'solid test.stl    2014-10-16 15:37:40.542327 halfdonut.stl                       \x01\x00\x00;\xa1n\xbc\xe6\xf5\x95=...@(\xefC>;\xc7p?\x00\x00\x83\xe1\x91=T\xf7e=H\xf2B\xbd7q6@(\xefC>;\xc7p?JA\x1d@\xe6\x04\xb5>\t\xfb\n?\x85\x080@\xe6\x04'
    @classmethod
    def _ascii_reader(cls, fh, header):
        if b'\n' in header:
            recoverable = [True]
        else:
            recoverable = [False]
            header += b(fh.read(BUFFER_SIZE))
    
        lines = b(header).split(b('\n'))
    
        def get(prefix=''):
            prefix = b(prefix)
    
            if lines:
                line = lines.pop(0)
            else:
                raise RuntimeError(recoverable[0], 'Unable to find more lines')
            if not lines:
                recoverable[0] = False
    
                # Read more lines and make sure we prepend any old data
                lines[:] = b(fh.read(BUFFER_SIZE)).split(b('\n'))
                line += lines.pop(0)
    
            line = line.lower().strip()
            if line == b(''):
                return get(prefix)
    
            if prefix:
                if line.startswith(prefix):
                    values = line.replace(prefix, b(''), 1).strip().split()
                elif line.startswith(b('endsolid')):
                    # go back to the beginning of new solid part
                    size_unprocessedlines = sum(len(l) + 1 for l in lines) - 1
                    if size_unprocessedlines > 0:
                        position = fh.tell()
                        fh.seek(position - size_unprocessedlines)
                    raise StopIteration()
                else:
                    raise RuntimeError(recoverable[0],
                                       '%r should start with %r' % (line,
                                                                    prefix))
    
                if len(values) == 3:
                    return [float(v) for v in values]
                else:  # pragma: no cover
                    raise RuntimeError(recoverable[0],
                                       'Incorrect value %r' % line)
            else:
                return b(line)
    
        line = get()
        if not line.startswith(b('solid ')) and line.startswith(b('solid')):
            cls.warning('ASCII STL files should start with solid <space>. '
                        'The application that produced this STL file may be '
                        'faulty, please report this error. The erroneous '
                        'line: %r', line)
    
        if not lines:
            raise RuntimeError(recoverable[0],
                               'No lines found, impossible to read')
    
        # Yield the name
        yield line[5:].strip()
    
        while True:
            # Read from the header lines first, until that point we can recover
            # and go to the binary option. After that we cannot due to
            # unseekable files such as sys.stdin
            #
            # Numpy doesn't support any non-file types so wrapping with a
            # buffer and/or StringIO does not work.
            try:
>               normals = get('facet normal')
stl/stl.py:197: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
prefix = b'facet normal'
    def get(prefix=''):
        prefix = b(prefix)
    
        if lines:
            line = lines.pop(0)
        else:
            raise RuntimeError(recoverable[0], 'Unable to find more lines')
        if not lines:
            recoverable[0] = False
    
            # Read more lines and make sure we prepend any old data
            lines[:] = b(fh.read(BUFFER_SIZE)).split(b('\n'))
            line += lines.pop(0)
    
        line = line.lower().strip()
        if line == b(''):
            return get(prefix)
    
        if prefix:
            if line.startswith(prefix):
                values = line.replace(prefix, b(''), 1).strip().split()
            elif line.startswith(b('endsolid')):
                # go back to the beginning of new solid part
                size_unprocessedlines = sum(len(l) + 1 for l in lines) - 1
                if size_unprocessedlines > 0:
                    position = fh.tell()
                    fh.seek(position - size_unprocessedlines)
                raise StopIteration()
            else:
                raise RuntimeError(recoverable[0],
                                   '%r should start with %r' % (line,
>                                                               prefix))
E               RuntimeError: (False, "b'!\\xbc\\xafw\\x7f>\\xe6\\x04\\xb5>\\x88e{?l\\x08\\x9e>f\\x83\\xec>\\x00\\x00\\xc0?\\xd1v\\xcc>f\\x83\\xec>\\xbe\\xa4\\x85?\\x00\\x00\\xb1\\xf5e\\xbd\\xed\\x13\\xac=\\x9f\\xf76\\xbc\\xafw\\x7f>\\xe6\\x04\\xb5>\\x88e{?4\\xf6\\x15>\\xe6\\x04\\xb5>\\x00\\x00\\xc0?l\\x08\\x9e>f\\x83\\xec>\\x00\\x00\\xc0?\\x00\\x00\\xfamj\\xbc[\\xf6\\x95=\\x9c,\\x07\\xbc\\x15x(?f\\x83\\xec>\\x15x(?\\x9a|\\x13?\\x00\\x00\\x00?w\\x04\\x8f?\\t\\xfbj?\\x00\\x00\\x00?\\t\\xfbj?\\x00\\x00c\\x02q\\xbc0\\xa8\\xb2=\\xcf\\t!\\xbc\\x15x(?f\\x83\\xec>\\x15x(?\\xd1v\\xcc>f\\x83\\xec>\\xbe\\xa4\\x85?\\x9a|\\x13?\\x00\\x00\\x00?w\\x04\\x8f?\\x00\\x00\\xfa\\x97#<\\xfa\\x88r=\\x85\\x9e\\xda;\\xfe\\x9dm?f\\x83\\xec>\\xfe\\x9dm?\\t\\xfbj?\\x00\\x00\\x00?\\t\\xfbj?\\xdc\\xbd@?f\\x83\\xec>\\x9dc\\x98?\\x00\\x00\\xfamj<\\x1f\\xf6\\x95=\\x9c,\\x07<\\xdc\\xbd@?f\\x83\\xec>\\x9dc\\x98?\\t\\xfbj?\\x00\\x00\\x00?\\t\\xfbj?\\x9a|\\x13?\\x00\\x00\\x00?w\\x04\\x8f?\\x00\\x00y8\\xba<\\x8c_$=\\xc0\\xd7x<k}\\x85?\\xe6\\x04\\xb5>k}\\x85?\\xdc\\xbd@?f\\x83\\xec>\\x9dc\\x98?x\\x1bg?\\xe6\\x04\\xb5>\\xdau\\xa0?\\x00\\x00\\xbc\\xf0\\xe8<\\x19\\x9bm=b\\xa5\\x9b<k}\\x85?\\xe6\\x04\\xb5>k}\\x85?\\xfe\\x9dm?f\\x83\\xec>\\xfe\\x9dm?\\xdc\\xbd@?f\\x83\\xec>\\x9dc\\x98?\\x00\\x00' should start with b'facet normal'")
stl/stl.py:165: RuntimeError
During handling of the above exception, another exception occurred:
binary_file = '/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'
tmpdir = local('/tmp/pytest-of-mockbuild/pytest-0/test_ascii_False_0')
speedups = False
    def test_ascii(binary_file, tmpdir, speedups):
        original_argv = sys.argv[:]
        try:
            sys.argv[:] = [
                'stl',
                '-s' if not speedups else '',
                binary_file,
                str(tmpdir.join('ascii.stl')),
            ]
            try:
>               main.to_ascii()
tests/test_commandline.py:49: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stl/main.py:73: in to_ascii
    speedups=not args.disable_speedups)
stl/stl.py:314: in from_file
    fh, mode=mode, speedups=speedups)
stl/stl.py:87: in load
    name, data = cls._load_binary(fh, header, check_size=True)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
header = b'solid test.stl    2014-10-16 15:37:40.542327 halfdonut.stl                      '
check_size = True
    @classmethod
    def _load_binary(cls, fh, header, check_size=False):
        # Read the triangle count
        count_data = fh.read(COUNT_SIZE)
        if len(count_data) != COUNT_SIZE:
            count = 0
        else:
            count, = struct.unpack(s('@i'), b(count_data))
        # raise RuntimeError()
        assert count < MAX_COUNT, ('File too large, got %d triangles which '
                                   'exceeds the maximum of %d') % (
>                                      count, MAX_COUNT)
E       AssertionError: File too large, got 536936448 triangles which exceeds the maximum of 100000000
stl/stl.py:104: AssertionError
__________________ test_mass_properties_for_half_donut[False] __________________
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
mode = <Mode.AUTOMATIC: 0>, speedups = False
    @classmethod
    def load(cls, fh, mode=AUTOMATIC, speedups=True):
        '''Load Mesh from STL file
    
            Automatically detects binary versus ascii STL files.
    
            :param file fh: The file handle to open
            :param int mode: Automatically detect the filetype or force binary
            '''
        header = fh.read(HEADER_SIZE).lower()
        if not header:
            return
    
        if isinstance(header, str):  # pragma: no branch
            header = b(header)
    
        name = ''
    
        if mode in (AUTOMATIC, ASCII) and header.startswith(b('solid')):
            try:
                name, data = cls._load_ascii(
>                   fh, header, speedups=speedups)
stl/stl.py:71: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
header = b'solid test.stl    2014-10-16 15:37:40.542327 halfdonut.stl                      '
speedups = False
    @classmethod
    def _load_ascii(cls, fh, header, speedups=True):
        if _speedups and speedups:
            return _speedups.ascii_read(fh, header)
        else:
            iterator = cls._ascii_reader(fh, header)
            name = next(iterator)
>           return name, numpy.fromiter(iterator, dtype=cls.dtype)
stl/stl.py:218: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
header = b'solid test.stl    2014-10-16 15:37:40.542327 halfdonut.stl                       \x01\x00\x00;\xa1n\xbc\xe6\xf5\x95=...@(\xefC>;\xc7p?\x00\x00\x83\xe1\x91=T\xf7e=H\xf2B\xbd7q6@(\xefC>;\xc7p?JA\x1d@\xe6\x04\xb5>\t\xfb\n?\x85\x080@\xe6\x04'
    @classmethod
    def _ascii_reader(cls, fh, header):
        if b'\n' in header:
            recoverable = [True]
        else:
            recoverable = [False]
            header += b(fh.read(BUFFER_SIZE))
    
        lines = b(header).split(b('\n'))
    
        def get(prefix=''):
            prefix = b(prefix)
    
            if lines:
                line = lines.pop(0)
            else:
                raise RuntimeError(recoverable[0], 'Unable to find more lines')
            if not lines:
                recoverable[0] = False
    
                # Read more lines and make sure we prepend any old data
                lines[:] = b(fh.read(BUFFER_SIZE)).split(b('\n'))
                line += lines.pop(0)
    
            line = line.lower().strip()
            if line == b(''):
                return get(prefix)
    
            if prefix:
                if line.startswith(prefix):
                    values = line.replace(prefix, b(''), 1).strip().split()
                elif line.startswith(b('endsolid')):
                    # go back to the beginning of new solid part
                    size_unprocessedlines = sum(len(l) + 1 for l in lines) - 1
                    if size_unprocessedlines > 0:
                        position = fh.tell()
                        fh.seek(position - size_unprocessedlines)
                    raise StopIteration()
                else:
                    raise RuntimeError(recoverable[0],
                                       '%r should start with %r' % (line,
                                                                    prefix))
    
                if len(values) == 3:
                    return [float(v) for v in values]
                else:  # pragma: no cover
                    raise RuntimeError(recoverable[0],
                                       'Incorrect value %r' % line)
            else:
                return b(line)
    
        line = get()
        if not line.startswith(b('solid ')) and line.startswith(b('solid')):
            cls.warning('ASCII STL files should start with solid <space>. '
                        'The application that produced this STL file may be '
                        'faulty, please report this error. The erroneous '
                        'line: %r', line)
    
        if not lines:
            raise RuntimeError(recoverable[0],
                               'No lines found, impossible to read')
    
        # Yield the name
        yield line[5:].strip()
    
        while True:
            # Read from the header lines first, until that point we can recover
            # and go to the binary option. After that we cannot due to
            # unseekable files such as sys.stdin
            #
            # Numpy doesn't support any non-file types so wrapping with a
            # buffer and/or StringIO does not work.
            try:
>               normals = get('facet normal')
stl/stl.py:197: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
prefix = b'facet normal'
    def get(prefix=''):
        prefix = b(prefix)
    
        if lines:
            line = lines.pop(0)
        else:
            raise RuntimeError(recoverable[0], 'Unable to find more lines')
        if not lines:
            recoverable[0] = False
    
            # Read more lines and make sure we prepend any old data
            lines[:] = b(fh.read(BUFFER_SIZE)).split(b('\n'))
            line += lines.pop(0)
    
        line = line.lower().strip()
        if line == b(''):
            return get(prefix)
    
        if prefix:
            if line.startswith(prefix):
                values = line.replace(prefix, b(''), 1).strip().split()
            elif line.startswith(b('endsolid')):
                # go back to the beginning of new solid part
                size_unprocessedlines = sum(len(l) + 1 for l in lines) - 1
                if size_unprocessedlines > 0:
                    position = fh.tell()
                    fh.seek(position - size_unprocessedlines)
                raise StopIteration()
            else:
                raise RuntimeError(recoverable[0],
                                   '%r should start with %r' % (line,
>                                                               prefix))
E               RuntimeError: (False, "b'!\\xbc\\xafw\\x7f>\\xe6\\x04\\xb5>\\x88e{?l\\x08\\x9e>f\\x83\\xec>\\x00\\x00\\xc0?\\xd1v\\xcc>f\\x83\\xec>\\xbe\\xa4\\x85?\\x00\\x00\\xb1\\xf5e\\xbd\\xed\\x13\\xac=\\x9f\\xf76\\xbc\\xafw\\x7f>\\xe6\\x04\\xb5>\\x88e{?4\\xf6\\x15>\\xe6\\x04\\xb5>\\x00\\x00\\xc0?l\\x08\\x9e>f\\x83\\xec>\\x00\\x00\\xc0?\\x00\\x00\\xfamj\\xbc[\\xf6\\x95=\\x9c,\\x07\\xbc\\x15x(?f\\x83\\xec>\\x15x(?\\x9a|\\x13?\\x00\\x00\\x00?w\\x04\\x8f?\\t\\xfbj?\\x00\\x00\\x00?\\t\\xfbj?\\x00\\x00c\\x02q\\xbc0\\xa8\\xb2=\\xcf\\t!\\xbc\\x15x(?f\\x83\\xec>\\x15x(?\\xd1v\\xcc>f\\x83\\xec>\\xbe\\xa4\\x85?\\x9a|\\x13?\\x00\\x00\\x00?w\\x04\\x8f?\\x00\\x00\\xfa\\x97#<\\xfa\\x88r=\\x85\\x9e\\xda;\\xfe\\x9dm?f\\x83\\xec>\\xfe\\x9dm?\\t\\xfbj?\\x00\\x00\\x00?\\t\\xfbj?\\xdc\\xbd@?f\\x83\\xec>\\x9dc\\x98?\\x00\\x00\\xfamj<\\x1f\\xf6\\x95=\\x9c,\\x07<\\xdc\\xbd@?f\\x83\\xec>\\x9dc\\x98?\\t\\xfbj?\\x00\\x00\\x00?\\t\\xfbj?\\x9a|\\x13?\\x00\\x00\\x00?w\\x04\\x8f?\\x00\\x00y8\\xba<\\x8c_$=\\xc0\\xd7x<k}\\x85?\\xe6\\x04\\xb5>k}\\x85?\\xdc\\xbd@?f\\x83\\xec>\\x9dc\\x98?x\\x1bg?\\xe6\\x04\\xb5>\\xdau\\xa0?\\x00\\x00\\xbc\\xf0\\xe8<\\x19\\x9bm=b\\xa5\\x9b<k}\\x85?\\xe6\\x04\\xb5>k}\\x85?\\xfe\\x9dm?f\\x83\\xec>\\xfe\\x9dm?\\xdc\\xbd@?f\\x83\\xec>\\x9dc\\x98?\\x00\\x00' should start with b'facet normal'")
stl/stl.py:165: RuntimeError
During handling of the above exception, another exception occurred:
ascii_path = local('/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_ascii')
binary_path = local('/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary')
speedups = False
    def test_mass_properties_for_half_donut(ascii_path, binary_path, speedups):
        """
        Checks the results of method get_mass_properties() on
        STL ASCII and binary files HalfDonut.stl
        One checks the results obtained with stl
        with the ones obtained with meshlab
        """
        for path in (ascii_path, binary_path):
            filename = path.join('HalfDonut.stl')
>           mesh = stl.StlMesh(str(filename), speedups=speedups)
tests/test_meshProperties.py:18: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stl/stl.py:318: in from_file
    fh, mode=mode, speedups=speedups)
stl/stl.py:87: in load
    name, data = cls._load_binary(fh, header, check_size=True)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
header = b'solid test.stl    2014-10-16 15:37:40.542327 halfdonut.stl                      '
check_size = True
    @classmethod
    def _load_binary(cls, fh, header, check_size=False):
        # Read the triangle count
        count_data = fh.read(COUNT_SIZE)
        if len(count_data) != COUNT_SIZE:
            count = 0
        else:
            count, = struct.unpack(s('@i'), b(count_data))
        # raise RuntimeError()
        assert count < MAX_COUNT, ('File too large, got %d triangles which '
                                   'exceeds the maximum of %d') % (
>                                      count, MAX_COUNT)
E       AssertionError: File too large, got 536936448 triangles which exceeds the maximum of 100000000
stl/stl.py:104: AssertionError
__________________ test_mass_properties_for_half_donut[True] ___________________
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
mode = <Mode.AUTOMATIC: 0>, speedups = True
    @classmethod
    def load(cls, fh, mode=AUTOMATIC, speedups=True):
        '''Load Mesh from STL file
    
            Automatically detects binary versus ascii STL files.
    
            :param file fh: The file handle to open
            :param int mode: Automatically detect the filetype or force binary
            '''
        header = fh.read(HEADER_SIZE).lower()
        if not header:
            return
    
        if isinstance(header, str):  # pragma: no branch
            header = b(header)
    
        name = ''
    
        if mode in (AUTOMATIC, ASCII) and header.startswith(b('solid')):
            try:
                name, data = cls._load_ascii(
>                   fh, header, speedups=speedups)
stl/stl.py:71: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
header = b'solid test.stl    2014-10-16 15:37:40.542327 halfdonut.stl                      '
speedups = True
    @classmethod
    def _load_ascii(cls, fh, header, speedups=True):
        if _speedups and speedups:
>           return _speedups.ascii_read(fh, header)
stl/stl.py:214: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
>   raise RuntimeError(state.recoverable,
E   RuntimeError: (0, "Can't read normals (2:b'!\\xbc\\xafw\\x7f>\\xe6\\x04\\xb5>\\x88e{?l\\x08\\x9e>f\\x83\\xec>')")
stl/_speedups.pyx:120: RuntimeError
During handling of the above exception, another exception occurred:
ascii_path = local('/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_ascii')
binary_path = local('/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary')
speedups = True
    def test_mass_properties_for_half_donut(ascii_path, binary_path, speedups):
        """
        Checks the results of method get_mass_properties() on
        STL ASCII and binary files HalfDonut.stl
        One checks the results obtained with stl
        with the ones obtained with meshlab
        """
        for path in (ascii_path, binary_path):
            filename = path.join('HalfDonut.stl')
>           mesh = stl.StlMesh(str(filename), speedups=speedups)
tests/test_meshProperties.py:18: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stl/stl.py:318: in from_file
    fh, mode=mode, speedups=speedups)
stl/stl.py:87: in load
    name, data = cls._load_binary(fh, header, check_size=True)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/HalfDonut.stl'>
header = b'solid test.stl    2014-10-16 15:37:40.542327 halfdonut.stl                      '
check_size = True
    @classmethod
    def _load_binary(cls, fh, header, check_size=False):
        # Read the triangle count
        count_data = fh.read(COUNT_SIZE)
        if len(count_data) != COUNT_SIZE:
            count = 0
        else:
            count, = struct.unpack(s('@i'), b(count_data))
        # raise RuntimeError()
        assert count < MAX_COUNT, ('File too large, got %d triangles which '
                                   'exceeds the maximum of %d') % (
>                                      count, MAX_COUNT)
E       AssertionError: File too large, got 536936448 triangles which exceeds the maximum of 100000000
stl/stl.py:104: AssertionError
_____________________ test_mass_properties_for_moon[False] _____________________
ascii_path = local('/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_ascii')
binary_path = local('/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary')
speedups = False
    def test_mass_properties_for_moon(ascii_path, binary_path, speedups):
        """
        Checks the results of method get_mass_properties() on
        STL ASCII and binary files Moon.stl
        One checks the results obtained with stl
        with the ones obtained with meshlab
        """
        for path in (ascii_path, binary_path):
            filename = path.join('Moon.stl')
>           mesh = stl.StlMesh(str(filename), speedups=speedups)
tests/test_meshProperties.py:40: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stl/stl.py:318: in from_file
    fh, mode=mode, speedups=speedups)
stl/stl.py:89: in load
    name, data = cls._load_binary(fh, header)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/Moon.stl'>
header = b'numpy-stl (1.0.0) 2014-10-16 15:37:40.642049 moon.stl                           '
check_size = False
    @classmethod
    def _load_binary(cls, fh, header, check_size=False):
        # Read the triangle count
        count_data = fh.read(COUNT_SIZE)
        if len(count_data) != COUNT_SIZE:
            count = 0
        else:
            count, = struct.unpack(s('@i'), b(count_data))
        # raise RuntimeError()
        assert count < MAX_COUNT, ('File too large, got %d triangles which '
                                   'exceeds the maximum of %d') % (
>                                      count, MAX_COUNT)
E       AssertionError: File too large, got 1946157056 triangles which exceeds the maximum of 100000000
stl/stl.py:104: AssertionError
_____________________ test_mass_properties_for_moon[True] ______________________
ascii_path = local('/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_ascii')
binary_path = local('/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary')
speedups = True
    def test_mass_properties_for_moon(ascii_path, binary_path, speedups):
        """
        Checks the results of method get_mass_properties() on
        STL ASCII and binary files Moon.stl
        One checks the results obtained with stl
        with the ones obtained with meshlab
        """
        for path in (ascii_path, binary_path):
            filename = path.join('Moon.stl')
>           mesh = stl.StlMesh(str(filename), speedups=speedups)
tests/test_meshProperties.py:40: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stl/stl.py:318: in from_file
    fh, mode=mode, speedups=speedups)
stl/stl.py:89: in load
    name, data = cls._load_binary(fh, header)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/Moon.stl'>
header = b'numpy-stl (1.0.0) 2014-10-16 15:37:40.642049 moon.stl                           '
check_size = False
    @classmethod
    def _load_binary(cls, fh, header, check_size=False):
        # Read the triangle count
        count_data = fh.read(COUNT_SIZE)
        if len(count_data) != COUNT_SIZE:
            count = 0
        else:
            count, = struct.unpack(s('@i'), b(count_data))
        # raise RuntimeError()
        assert count < MAX_COUNT, ('File too large, got %d triangles which '
                                   'exceeds the maximum of %d') % (
>                                      count, MAX_COUNT)
E       AssertionError: File too large, got 1946157056 triangles which exceeds the maximum of 100000000
stl/stl.py:104: AssertionError
_____________________ test_mass_properties_for_star[False] _____________________
ascii_path = local('/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_ascii')
binary_path = local('/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary')
speedups = False
    def test_mass_properties_for_star(ascii_path, binary_path, speedups):
        """
        Checks the results of method get_mass_properties() on
        STL ASCII and binary files Star.stl and
        STL binary file StarWithEmptyHeader.stl (with no header)
        One checks the results obtained with stl
        with the ones obtained with meshlab
        """
        for filename in (ascii_path.join('Star.stl'),
                         binary_path.join('Star.stl'),
                         binary_path.join('StarWithEmptyHeader.stl')):
>           mesh = stl.StlMesh(str(filename), speedups=speedups)
tests/test_meshProperties.py:64: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stl/stl.py:318: in from_file
    fh, mode=mode, speedups=speedups)
stl/stl.py:89: in load
    name, data = cls._load_binary(fh, header)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/Star.stl'>
header = b'numpy-stl (1.0.0) 2014-10-16 15:37:40.741314 star.stl                           '
check_size = False
    @classmethod
    def _load_binary(cls, fh, header, check_size=False):
        # Read the triangle count
        count_data = fh.read(COUNT_SIZE)
        if len(count_data) != COUNT_SIZE:
            count = 0
        else:
            count, = struct.unpack(s('@i'), b(count_data))
        # raise RuntimeError()
        assert count < MAX_COUNT, ('File too large, got %d triangles which '
                                   'exceeds the maximum of %d') % (
>                                      count, MAX_COUNT)
E       AssertionError: File too large, got 1140850688 triangles which exceeds the maximum of 100000000
stl/stl.py:104: AssertionError
_____________________ test_mass_properties_for_star[True] ______________________
ascii_path = local('/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_ascii')
binary_path = local('/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary')
speedups = True
    def test_mass_properties_for_star(ascii_path, binary_path, speedups):
        """
        Checks the results of method get_mass_properties() on
        STL ASCII and binary files Star.stl and
        STL binary file StarWithEmptyHeader.stl (with no header)
        One checks the results obtained with stl
        with the ones obtained with meshlab
        """
        for filename in (ascii_path.join('Star.stl'),
                         binary_path.join('Star.stl'),
                         binary_path.join('StarWithEmptyHeader.stl')):
>           mesh = stl.StlMesh(str(filename), speedups=speedups)
tests/test_meshProperties.py:64: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stl/stl.py:318: in from_file
    fh, mode=mode, speedups=speedups)
stl/stl.py:89: in load
    name, data = cls._load_binary(fh, header)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'stl.stl.BaseStl'>
fh = <_io.BufferedReader name='/builddir/build/BUILD/numpy-stl-2.1.0/tests/stl_binary/Star.stl'>
header = b'numpy-stl (1.0.0) 2014-10-16 15:37:40.741314 star.stl                           '
check_size = False
    @classmethod
    def _load_binary(cls, fh, header, check_size=False):
        # Read the triangle count
        count_data = fh.read(COUNT_SIZE)
        if len(count_data) != COUNT_SIZE:
            count = 0
        else:
            count, = struct.unpack(s('@i'), b(count_data))
        # raise RuntimeError()
        assert count < MAX_COUNT, ('File too large, got %d triangles which '
                                   'exceeds the maximum of %d') % (
>                                      count, MAX_COUNT)
E       AssertionError: File too large, got 1140850688 triangles which exceeds the maximum of 100000000
stl/stl.py:104: AssertionError
===================== 9 failed, 49 passed in 1.11 seconds ======================

I think that whenever you are unpacking struct with @, you should use < instead. For example here. Will investigate.

Add license

setup.py says the package is BSD licensed, but it would be good to have a LICENSE file in the repo.

loading an stl file

@wolph I tried to run the example but I get an error message stating that "mesh" in the mesh.Mesh_from_file method is not defined. Also, I noticed there was nothing in the stl/mesh module

import numpy
from stl import mesh
your_mesh = mesh.Mesh.from_file('some_file.stl')

I tried modifying the code but I get an error message stating that "module object has no attribute "from file" "

import numpy
import stl
your_mesh = stl.from_file('some_file.stl')

Handling more than 1M triangles, when to use mesh.points

Numpy-stl has been helping me quite a bit recently with some operations that I can't do in typical CAD programs. I've been mostly using it to parse STLs and then write my own algorithms to modify those files. It has been very helpful in this regard, and I am very thankful that you've already done most of the heavy lifting for playing with STLs in python.

I work with a lot of large (>1M triangle) meshes. I've gone into my stl.py file and raised the MAX_COUNT to higher numbers when it has been necessary, and I haven't had any problems when doing so. I was wondering why the 1M max triangle count was in place, and which limitations made that necessary. I'm doing simple 1-at-a-time vector manipulation, but I could understand that some of the fancier stuff will require more memory or computational load.

Finally, when I request the iterable mesh.points, I expect an array of 1x3 arrays that is 3x the size of mesh.vectors. Instead, I get an array of the same size as mesh.vectors with 1x9 arrays. I was wondering what the rationale was for this, because I can't see a time when one would use mesh.points over mesh.vectors, since the latter is simply a better organized version of the former. Iterating over mesh.points doesn't actually iterate over all points, which seems odd to me, but I also understand that iterating over triangles makes sense for this sort of thing.

Thanks in advance for the help, and for creating such a brilliant module.

Getting list of points without repetition

I see that there is no method to obtain the point cloud, i.e, all the vertices in the mesh without repetition. Currently I am doing this brute force, checking through all points. Is there an efficient way or am I missing a method?

from stl import mesh fails

Hi,

I am on windows 10 and installed numpy-stl per https://pypi.python.org/pypi/numpy-stl .When I run any samples I get following error at line
"from stl import mesh"
ImportError: cannot import name 'mesh'

I could see mesh.py under stl folder in my site-packages.

Please help me fix this issue.

regards
Raghavendra

Saving scaled stl file

hi,

Is numpy-stl capable of saving a scaled stl file?
I mean I opened an stl file, then scaled it.
Will I be able to save this scaled object as another stl file?

Thanks in advance.

some facets are lost during open and save procedure

I was looking at using this as a pre-requisite for one of my projects. I noticed that if a mesh is opened and saved, a number of facets are lost. There is no error text, or indication that something didn't go well. Any ideas?

HEADER_SIZE = 80 is stringent at my case

Hello Mr Wolph,

The default 80 bytes, i.e. 80 characters were quite stringent for me in my use case as I had some stl parts with more characters. Would you consider increasing it to 256? I think it would not affect the speed adversely too much.

Cheers
Lazlo

help for stl-modification

Hello, thanks for that great package!

but i still have problems to understand the structure. i want to open a stl from file, then search for points which have a z coordinate > 0, select these points and save the selected points as a new stl-file.
how would you proceed?

thanks!

Rotate (and other manipulation) functions

Hi,

I can't find a function to rotate a mesh. What is the standard way to rotate a mesh?

It would be convenient to expose functions like:

  • translate
  • scale
  • rotate

Thanks for creating this lib!
Hans

Add configurable rotation point to the rotation function

Since we are calculating a transformation matrix in homogeneous coordinates it would be fairly trivial to replace a translate, rotate, translate operation by a single matrix transformation. Calculating the dot product of a 4x4 matrix would be far lighter than calculating the dot product for an Nx3

Add possibility to rotate through custom rotation matrices

Copied from wolph's blog: https://w.wol.ph/about/#comment-174

First, many thanks for your numpy-stl module. Most helpful. I have one suggestion to make:

The only rotation you have enabled in your code is around an arbitrary axis (and a point), which is super helpful. I understand that you are using the rotation_matrix function which calculates the appropriate rotation matrix, which you then use to rotate the stl.

I would find it very useful (and powerful) to provide a second function that would allow a rotation around a user supplied rotation matrix.

What I am thinking is this:

rollMatrix = mesh.Mesh.rotation_matrix((1,0,0), heel_rads)
pitchMatrix = mesh.Mesh.rotation_matrix((0,1,0), pitch_rads)
combinedMatrix = numpy.dot(rollMatrix, pitchMatrix)
my_mesh.rotate_using_matrix(combinedMatrix)

This would allow for very easy chaining of matrices.

The work-around is very easy, but this seems like a really quick and useful addition…

Error: Struct() argument 1 must be string, not unicode using mesh.Mesh.from_file()

Hello,
I get the following error using the command line functions:

$ stl2ascii right_hand_pointing.stl right_hand_pointing_bin.stl  
Traceback (most recent call last):
  File "/usr/local/bin/stl2ascii", line 9, in <module>
    load_entry_point('numpy-stl==1.5.0', 'console_scripts', 'stl2ascii')()
  File "/usr/local/lib/python2.7/dist-packages/stl/main.py", line 68, in to_ascii
    remove_empty_areas=args.remove_empty_areas)
  File "/usr/local/lib/python2.7/dist-packages/stl/stl.py", line 270, in from_file
    name, data = cls.load(fh, mode=mode)
  File "/usr/local/lib/python2.7/dist-packages/stl/stl.py", line 70, in load
    data = cls._load_binary(fh, header, check_size=True)
  File "/usr/local/lib/python2.7/dist-packages/stl/stl.py", line 79, in _load_binary
    count, = struct.unpack('@i', b(fh.read(COUNT_SIZE)))
TypeError: Struct() argument 1 must be string, not unicode

I also get this problem when using the api, as in the example:

your_mesh = mesh.Mesh.from_file('right_hand_pointing.stl')

I am using the newest version of python-utils, with python 2.7.3. Is this correct?

Thanks!
Kel

rotate function typo

silly mistake, but the Euler-Rodrigues matrix is incomplete.
The variables are assigned like:
aa, ab, ac,ad = powers[0:4]
BB, bb, bc, bd = powers[4:8]
CC, cb, cc, cd = powers[8:12]
DD, db, dc, dd = powers[12:16]

Since ba = ab, ca = ac and da = ad; there are no issues in the calculations stemming from this, but it's still a bit silly. ;)

In the same category of silly:

Rotate 90 degrees over the X axis

meshes[0].rotate([0.0, 0.5, 0.0], math.radians(90)) <- This should be [1., 0., 0.] right? Else you rotate about the y axis.
(and the one right after as well)

ASCII versus Binary detection and fallback

An outstanding issue is some corruption/errors in an ASCII STL file will cause numpy-stl to fallback to a binary load, which can lead to the MAX_COUNT overrun exception. The ASCII STL errors that cause this include:

  • An extra vertex in a facet
  • A missing vertex in a facet

Strictly speaking, the smallest complete ASCII STL file is, I believe, 186 bytes. This file would include the following keywords:
solid facet normal outer loop vertex endloop endfacet endsolid
along with the normal and vertex values.

I think that the following decision tree should be included, for a strict ASCII/Binary determination:

  • File includes all keywords from above in standard UTF-8 ASCII outside of the header/solid name
    • ASCII
  • File includes some but not all of the above keywords
    • Corruption warning, or perhaps some extra checks.
  • File size < 196 bytes
    • Extra validation, or default to Binary
  • Else
    • Binary

There would be no fallback to Binary after an ASCII parsing error. While it is technically true that a Binary file could contain all of the above keywords, the odds are low that it would be a valid STL file (I haven't checked this) and that this would occur from an actual STL file not designed to break numpy-stl.

We can then remove the MAX_COUNT arbitrary cutoff. The loading process, I think, would be greatly simplified, and some better exceptions can be thrown (more descriptive; can be recoverable/unrecoverable; can add a loading mode of "strict"/"flexible"; etc.).

As an example, PyCam did it this way when they implemented their own STL loader.

Numpy-stl not installable under Windows 7 and Python 3

The numpy-stl package cannot be installed with Python 3 under Windows, since Cython does not
fully support Python 3. (At least v2.2.3 has this problem.) In the
"stl/_speedups.pyx" file the statement in line 4
IF UNAME_SYSNAME == u"Windows":
is not evaluated correctly.
Cheers, dietrich

PS: Tested on Window7, Python 3.6, Cython 0.25.2 (Anaconda 4.3)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.