tests and examples¶
tests.stl_corruption module¶
from __future__ import print_function
import pytest
import struct
from stl import mesh
_STL_FILE = '''
solid test.stl
facet normal -0.014565 0.073223 -0.002897
outer loop
vertex 0.399344 0.461940 1.044090
vertex 0.500000 0.500000 1.500000
vertex 0.576120 0.500000 1.117320
endloop
endfacet
endsolid test.stl
'''.lstrip()
def test_valid_ascii(tmpdir, speedups):
tmp_file = tmpdir.join('tmp.stl')
with tmp_file.open('w+') as fh:
fh.write(_STL_FILE)
fh.seek(0)
mesh.Mesh.from_file(str(tmp_file), fh=fh, speedups=speedups)
def test_ascii_with_missing_name(tmpdir, speedups):
tmp_file = tmpdir.join('tmp.stl')
with tmp_file.open('w+') as fh:
# Split the file into lines
lines = _STL_FILE.splitlines()
# Remove everything except solid
lines[0] = lines[0].split()[0]
# Join the lines to test files that start with solid without space
fh.write('\n'.join(lines))
fh.seek(0)
mesh.Mesh.from_file(str(tmp_file), fh=fh, speedups=speedups)
def test_ascii_with_blank_lines(tmpdir, speedups):
_stl_file = '''
solid test.stl
facet normal -0.014565 0.073223 -0.002897
outer loop
vertex 0.399344 0.461940 1.044090
vertex 0.500000 0.500000 1.500000
vertex 0.576120 0.500000 1.117320
endloop
endfacet
endsolid test.stl
'''.lstrip()
tmp_file = tmpdir.join('tmp.stl')
with tmp_file.open('w+') as fh:
fh.write(_stl_file)
fh.seek(0)
mesh.Mesh.from_file(str(tmp_file), fh=fh, speedups=speedups)
def test_incomplete_ascii_file(tmpdir, speedups):
tmp_file = tmpdir.join('tmp.stl')
with tmp_file.open('w+') as fh:
fh.write('solid some_file.stl')
fh.seek(0)
with pytest.raises(AssertionError):
mesh.Mesh.from_file(str(tmp_file), fh=fh, speedups=speedups)
for offset in (-20, 82, 100):
with tmp_file.open('w+') as fh:
fh.write(_STL_FILE[:-offset])
fh.seek(0)
with pytest.raises(AssertionError):
mesh.Mesh.from_file(str(tmp_file), fh=fh, speedups=speedups)
def test_corrupt_ascii_file(tmpdir, speedups):
tmp_file = tmpdir.join('tmp.stl')
with tmp_file.open('w+') as fh:
fh.write(_STL_FILE)
fh.seek(40)
print('####\n' * 100, file=fh)
fh.seek(0)
with pytest.raises(AssertionError):
mesh.Mesh.from_file(str(tmp_file), fh=fh, speedups=speedups)
with tmp_file.open('w+') as fh:
fh.write(_STL_FILE)
fh.seek(40)
print(' ' * 100, file=fh)
fh.seek(80)
fh.write(struct.pack('<i', 10).decode('utf-8'))
fh.seek(0)
with pytest.raises(AssertionError):
mesh.Mesh.from_file(str(tmp_file), fh=fh, speedups=speedups)
def test_corrupt_binary_file(tmpdir, speedups):
tmp_file = tmpdir.join('tmp.stl')
with tmp_file.open('w+') as fh:
fh.write('#########\n' * 8)
fh.write('#\0\0\0')
fh.seek(0)
mesh.Mesh.from_file(str(tmp_file), fh=fh, speedups=speedups)
with tmp_file.open('w+') as fh:
fh.write('#########\n' * 9)
fh.seek(0)
with pytest.raises(AssertionError):
mesh.Mesh.from_file(str(tmp_file), fh=fh, speedups=speedups)
with tmp_file.open('w+') as fh:
fh.write('#########\n' * 8)
fh.write('#\0\0\0')
fh.seek(0)
fh.write('solid test.stl')
fh.seek(0)
mesh.Mesh.from_file(str(tmp_file), fh=fh, speedups=speedups)
tests.test_commandline module¶
import sys
from stl import main
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()
sys.argv[:] = args_pre + ['-b', ascii_file] + args_post
main.main()
finally:
sys.argv[:] = original_argv
def test_args(ascii_file, tmpdir):
parser = main._get_parser('')
def _get_name(*args):
return main._get_name(parser.parse_args(list(map(str, args))))
assert _get_name('--name', 'foobar') == 'foobar'
assert _get_name('-', tmpdir.join('binary.stl')).endswith('binary.stl')
assert _get_name(ascii_file, '-').endswith('HalfDonut.stl')
assert _get_name('-', '-')
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()
except SystemExit:
pass
finally:
sys.argv[:] = original_argv
def test_binary(ascii_file, tmpdir, speedups):
original_argv = sys.argv[:]
try:
sys.argv[:] = [
'stl',
'-s' if not speedups else '',
ascii_file,
str(tmpdir.join('binary.stl')),
]
try:
main.to_binary()
except SystemExit:
pass
finally:
sys.argv[:] = original_argv
tests.test_convert module¶
# import os
import pytest
import tempfile
from stl import stl
def _test_conversion(from_, to, mode, speedups):
for name in from_.listdir():
source_file = from_.join(name)
expected_file = to.join(name)
if not expected_file.exists():
continue
mesh = stl.StlMesh(source_file, speedups=speedups)
with open(str(expected_file), 'rb') as expected_fh:
expected = expected_fh.read()
# For binary files, skip the header
if mode is stl.BINARY:
expected = expected[80:]
with tempfile.TemporaryFile() as dest_fh:
mesh.save(name, dest_fh, mode)
# Go back to the beginning to read
dest_fh.seek(0)
dest = dest_fh.read()
# For binary files, skip the header
if mode is stl.BINARY:
dest = dest[80:]
assert dest.strip() == expected.strip()
def test_ascii_to_binary(ascii_path, binary_path, speedups):
_test_conversion(ascii_path, binary_path, mode=stl.BINARY,
speedups=speedups)
def test_binary_to_ascii(ascii_path, binary_path, speedups):
_test_conversion(binary_path, ascii_path, mode=stl.ASCII,
speedups=speedups)
def test_stl_mesh(ascii_file, tmpdir, speedups):
tmp_file = tmpdir.join('tmp.stl')
mesh = stl.StlMesh(ascii_file, speedups=speedups)
with pytest.raises(ValueError):
mesh.save(filename=str(tmp_file), mode='test')
mesh.save(str(tmp_file))
mesh.save(str(tmp_file), update_normals=False)
tests.test_mesh module¶
import numpy
from stl.mesh import Mesh
from stl.base import BaseMesh
from stl.base import RemoveDuplicates
def test_units_1d():
data = numpy.zeros(1, dtype=Mesh.dtype)
data['vectors'][0] = numpy.array([[0, 0, 0],
[1, 0, 0],
[2, 0, 0]])
mesh = Mesh(data, remove_empty_areas=False)
mesh.update_units()
assert mesh.areas == 0
assert (mesh.normals == [0, 0, 0]).all()
assert (mesh.units == [0, 0, 0]).all()
def test_units_2d():
data = numpy.zeros(2, dtype=Mesh.dtype)
data['vectors'][0] = numpy.array([[0, 0, 0],
[1, 0, 0],
[0, 1, 0]])
data['vectors'][1] = numpy.array([[1, 0, 0],
[0, 1, 0],
[1, 1, 0]])
mesh = Mesh(data, remove_empty_areas=False)
mesh.update_units()
assert (mesh.areas == [.5, .5]).all()
assert (mesh.normals == [[0, 0, 1.],
[0, 0, -1.]]).all()
assert (mesh.units == [[0, 0, 1],
[0, 0, -1]]).all()
def test_units_3d():
data = numpy.zeros(1, dtype=Mesh.dtype)
data['vectors'][0] = numpy.array([[0, 0, 0],
[1, 0, 0],
[0, 1, 1.]])
mesh = Mesh(data, remove_empty_areas=False)
mesh.update_units()
assert (mesh.areas - 2 ** .5) < 0.0001
assert (mesh.normals == [0, -1, 1]).all()
units = mesh.units[0]
assert units[0] == 0
# Due to floating point errors
assert (units[1] + .5 * 2 ** .5) < 0.0001
assert (units[2] - .5 * 2 ** .5) < 0.0001
def test_duplicate_polygons():
data = numpy.zeros(6, dtype=Mesh.dtype)
data['vectors'][0] = numpy.array([[1, 0, 0],
[0, 0, 0],
[0, 0, 0]])
data['vectors'][1] = numpy.array([[2, 0, 0],
[0, 0, 0],
[0, 0, 0]])
data['vectors'][2] = numpy.array([[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
data['vectors'][3] = numpy.array([[2, 0, 0],
[0, 0, 0],
[0, 0, 0]])
data['vectors'][4] = numpy.array([[1, 0, 0],
[0, 0, 0],
[0, 0, 0]])
data['vectors'][5] = numpy.array([[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
mesh = Mesh(data)
assert mesh.data.size == 6
mesh = Mesh(data, remove_duplicate_polygons=0)
assert mesh.data.size == 6
mesh = Mesh(data, remove_duplicate_polygons=False)
assert mesh.data.size == 6
mesh = Mesh(data, remove_duplicate_polygons=None)
assert mesh.data.size == 6
mesh = Mesh(data, remove_duplicate_polygons=RemoveDuplicates.NONE)
assert mesh.data.size == 6
mesh = Mesh(data, remove_duplicate_polygons=RemoveDuplicates.SINGLE)
assert mesh.data.size == 3
mesh = Mesh(data, remove_duplicate_polygons=True)
assert mesh.data.size == 3
assert (mesh.vectors[0] == numpy.array([[1, 0, 0],
[0, 0, 0],
[0, 0, 0]])).all()
assert (mesh.vectors[1] == numpy.array([[2, 0, 0],
[0, 0, 0],
[0, 0, 0]])).all()
assert (mesh.vectors[2] == numpy.array([[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])).all()
mesh = Mesh(data, remove_duplicate_polygons=RemoveDuplicates.ALL)
assert mesh.data.size == 3
assert (mesh.vectors[0] == numpy.array([[1, 0, 0],
[0, 0, 0],
[0, 0, 0]])).all()
assert (mesh.vectors[1] == numpy.array([[2, 0, 0],
[0, 0, 0],
[0, 0, 0]])).all()
assert (mesh.vectors[2] == numpy.array([[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])).all()
def test_remove_all_duplicate_polygons():
data = numpy.zeros(5, dtype=Mesh.dtype)
data['vectors'][0] = numpy.array([[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
data['vectors'][1] = numpy.array([[1, 0, 0],
[0, 0, 0],
[0, 0, 0]])
data['vectors'][2] = numpy.array([[2, 0, 0],
[0, 0, 0],
[0, 0, 0]])
data['vectors'][3] = numpy.array([[3, 0, 0],
[0, 0, 0],
[0, 0, 0]])
data['vectors'][4] = numpy.array([[3, 0, 0],
[0, 0, 0],
[0, 0, 0]])
mesh = Mesh(data, remove_duplicate_polygons=False)
assert mesh.data.size == 5
Mesh.remove_duplicate_polygons(mesh.data, RemoveDuplicates.NONE)
mesh = Mesh(data, remove_duplicate_polygons=RemoveDuplicates.ALL)
assert mesh.data.size == 3
assert (mesh.vectors[0] == numpy.array([[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])).all()
assert (mesh.vectors[1] == numpy.array([[1, 0, 0],
[0, 0, 0],
[0, 0, 0]])).all()
assert (mesh.vectors[2] == numpy.array([[2, 0, 0],
[0, 0, 0],
[0, 0, 0]])).all()
def test_empty_areas():
data = numpy.zeros(3, dtype=Mesh.dtype)
data['vectors'][0] = numpy.array([[0, 0, 0],
[1, 0, 0],
[0, 1, 0]])
data['vectors'][1] = numpy.array([[1, 0, 0],
[0, 1, 0],
[1, 0, 0]])
data['vectors'][2] = numpy.array([[1, 0, 0],
[0, 1, 0],
[1, 0, 0]])
mesh = Mesh(data, remove_empty_areas=False)
assert mesh.data.size == 3
mesh = Mesh(data, remove_empty_areas=True)
assert mesh.data.size == 1
def test_base_mesh():
data = numpy.zeros(10, dtype=BaseMesh.dtype)
mesh = BaseMesh(data, remove_empty_areas=False)
# Increment vector 0 item 0
mesh.v0[0] += 1
mesh.v1[0] += 2
# Check item 0 (contains v0, v1 and v2)
assert (mesh[0] == numpy.array(
[1., 1., 1., 2., 2., 2., 0., 0., 0.], dtype=numpy.float32)
).all()
assert (mesh.vectors[0] == numpy.array([
[1., 1., 1.],
[2., 2., 2.],
[0., 0., 0.]], dtype=numpy.float32)).all()
assert (mesh.v0[0] == numpy.array([1., 1., 1.], dtype=numpy.float32)).all()
assert (mesh.points[0] == numpy.array(
[1., 1., 1., 2., 2., 2., 0., 0., 0.], dtype=numpy.float32)
).all()
assert (
mesh.x[0] == numpy.array([1., 2., 0.], dtype=numpy.float32)).all()
mesh[0] = 3
assert (mesh[0] == numpy.array(
[3., 3., 3., 3., 3., 3., 3., 3., 3.], dtype=numpy.float32)
).all()
assert len(mesh) == len(list(mesh))
assert (mesh.min_ < mesh.max_).all()
mesh.update_normals()
assert mesh.units.sum() == 0.0
mesh.v0[:] = mesh.v1[:] = mesh.v2[:] = 0
assert mesh.points.sum() == 0.0
tests.test_multiple module¶
from stl import mesh
from stl.utils import b
_STL_FILE = b('''
solid test.stl
facet normal -0.014565 0.073223 -0.002897
outer loop
vertex 0.399344 0.461940 1.044090
vertex 0.500000 0.500000 1.500000
vertex 0.576120 0.500000 1.117320
endloop
endfacet
endsolid test.stl
'''.lstrip())
def test_single_stl(tmpdir, speedups):
tmp_file = tmpdir.join('tmp.stl')
with tmp_file.open('wb+') as fh:
fh.write(_STL_FILE)
fh.seek(0)
for m in mesh.Mesh.from_multi_file(
str(tmp_file), fh=fh, speedups=speedups):
pass
def test_multiple_stl(tmpdir, speedups):
tmp_file = tmpdir.join('tmp.stl')
with tmp_file.open('wb+') as fh:
for _ in range(10):
fh.write(_STL_FILE)
fh.seek(0)
for i, m in enumerate(mesh.Mesh.from_multi_file(
str(tmp_file), fh=fh, speedups=speedups)):
assert m.name == b'test.stl'
assert i == 9
def test_single_stl_file(tmpdir, speedups):
tmp_file = tmpdir.join('tmp.stl')
with tmp_file.open('wb+') as fh:
fh.write(_STL_FILE)
fh.seek(0)
for m in mesh.Mesh.from_multi_file(
str(tmp_file), speedups=speedups):
pass
def test_multiple_stl_file(tmpdir, speedups):
tmp_file = tmpdir.join('tmp.stl')
with tmp_file.open('wb+') as fh:
for _ in range(10):
fh.write(_STL_FILE)
fh.seek(0)
for i, m in enumerate(mesh.Mesh.from_multi_file(
str(tmp_file), speedups=speedups)):
assert m.name == b'test.stl'
assert i == 9
tests.test_rotate module¶
import math
import numpy
from stl.mesh import Mesh
def test_rotation():
# Create 6 faces of a cube
data = numpy.zeros(6, dtype=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]])
# Right 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]])
mesh = Mesh(data, remove_empty_areas=False)
# Since the cube faces are from 0 to 1 we can move it to the middle by
# substracting .5
data['vectors'] -= .5
# Rotate 90 degrees over the X axis followed by the Y axis followed by the
# X axis
mesh.rotate([0.5, 0.0, 0.0], math.radians(90))
mesh.rotate([0.0, 0.5, 0.0], math.radians(90))
mesh.rotate([0.5, 0.0, 0.0], math.radians(90))
# Since the cube faces are from 0 to 1 we can move it to the middle by
# substracting .5
data['vectors'] += .5
assert (mesh.vectors == numpy.array([
[[1, 0, 0], [0, 1, 0], [0, 0, 0]],
[[0, 1, 0], [1, 0, 0], [1, 1, 0]],
[[0, 1, 1], [0, 1, 0], [1, 1, 1]],
[[1, 1, 0], [0, 1, 0], [1, 1, 1]],
[[0, 0, 1], [0, 1, 1], [0, 1, 0]],
[[0, 0, 1], [0, 0, 0], [0, 1, 0]],
])).all()
def test_rotation_over_point():
# Create a single face
data = numpy.zeros(1, dtype=Mesh.dtype)
data['vectors'][0] = numpy.array([[1, 0, 0],
[0, 1, 0],
[0, 0, 1]])
mesh = Mesh(data, remove_empty_areas=False)
mesh.rotate([1, 0, 0], math.radians(180), point=[1, 2, 3])
assert (mesh.vectors == numpy.array([[1, -4, -6],
[0, -5, -6],
[0, -4, -7]])).all()
def test_no_rotation():
# Create a single face
data = numpy.zeros(1, dtype=Mesh.dtype)
data['vectors'][0] = numpy.array([[0, 1, 1],
[1, 0, 1],
[0, 0, 1]])
mesh = Mesh(data, remove_empty_areas=False)
# Rotate by 0 degrees
mesh.rotate([0.5, 0.0, 0.0], math.radians(0))
assert (mesh.vectors == numpy.array([
[[0, 1, 1], [1, 0, 1], [0, 0, 1]]])).all()
# Use a zero rotation matrix
mesh.rotate([0.0, 0.0, 0.0], math.radians(90))
assert (mesh.vectors == numpy.array([
[[0, 1, 1], [1, 0, 1], [0, 0, 1]]])).all()
def test_no_translation():
# Create a single face
data = numpy.zeros(1, dtype=Mesh.dtype)
data['vectors'][0] = numpy.array([[0, 1, 1],
[1, 0, 1],
[0, 0, 1]])
mesh = Mesh(data, remove_empty_areas=False)
assert (mesh.vectors == numpy.array([
[[0, 1, 1], [1, 0, 1], [0, 0, 1]]])).all()
# Translate mesh with a zero vector
mesh.translate([0.0, 0.0, 0.0])
assert (mesh.vectors == numpy.array([
[[0, 1, 1], [1, 0, 1], [0, 0, 1]]])).all()
def test_translation():
# Create a single face
data = numpy.zeros(1, dtype=Mesh.dtype)
data['vectors'][0] = numpy.array([[0, 1, 1],
[1, 0, 1],
[0, 0, 1]])
mesh = Mesh(data, remove_empty_areas=False)
assert (mesh.vectors == numpy.array([
[[0, 1, 1], [1, 0, 1], [0, 0, 1]]])).all()
# Translate mesh with vector [1, 2, 3]
mesh.translate([1.0, 2.0, 3.0])
assert (mesh.vectors == numpy.array([
[[1, 3, 4], [2, 2, 4], [1, 2, 4]]])).all()
def test_no_transformation():
# Create a single face
data = numpy.zeros(1, dtype=Mesh.dtype)
data['vectors'][0] = numpy.array([[0, 1, 1],
[1, 0, 1],
[0, 0, 1]])
mesh = Mesh(data, remove_empty_areas=False)
assert (mesh.vectors == numpy.array([
[[0, 1, 1], [1, 0, 1], [0, 0, 1]]])).all()
# Transform mesh with identity matrix
mesh.transform(numpy.eye(4))
assert (mesh.vectors == numpy.array([
[[0, 1, 1], [1, 0, 1], [0, 0, 1]]])).all()
assert numpy.all(mesh.areas == 0.5)
def test_transformation():
# Create a single face
data = numpy.zeros(1, dtype=Mesh.dtype)
data['vectors'][0] = numpy.array([[0, 1, 1],
[1, 0, 1],
[0, 0, 1]])
mesh = Mesh(data, remove_empty_areas=False)
assert (mesh.vectors == numpy.array([
[[0, 1, 1], [1, 0, 1], [0, 0, 1]]])).all()
# Transform mesh with identity matrix
tr = numpy.zeros((4, 4))
tr[0:3, 0:3] = Mesh.rotation_matrix([0, 0, 1], 0.5 * numpy.pi)
tr[0:3, 3] = [1, 2, 3]
mesh.transform(tr)
assert (mesh.vectors == numpy.array([
[[0, 2, 4], [1, 3, 4], [1, 2, 4]]])).all()
assert numpy.all(mesh.areas == 0.5)