Skip to content

Commit

Permalink
Merge branch 'release/2.4.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
wolph committed Mar 10, 2018
2 parents 75320b1 + 598ffbf commit 443c8e5
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 46 deletions.
4 changes: 4 additions & 0 deletions .pyup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# autogenerated pyup.io config file
# see https://pyup.io/docs/configuration/ for all available options

update: insecure
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ script:
- tox -e $TOX_ENV
after_success:
- coveralls
- pip install codecov
- codecov
before_script: flake8 --ignore=W391 stl tests
notifications:
email:
Expand Down
21 changes: 11 additions & 10 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -341,16 +341,17 @@ Combining multiple STL files
def translate(_solid, step, padding, multiplier, axis):
if axis == 'x':
items = [0, 3, 6]
elif axis == 'y':
items = [1, 4, 7]
elif axis == 'z':
items = [2, 5, 8]
for p in _solid.points:
# point items are ((x, y, z), (x, y, z), (x, y, z))
for i in range(3):
p[items[i]] += (step * multiplier) + (padding * multiplier)
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):
Expand Down
7 changes: 7 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[pytest]
basetemp = tmp

doctest_optionflags = NORMALIZE_WHITESPACE

python_files =
stl/*.py
tests/*.py
Expand All @@ -21,3 +23,8 @@ pep8ignore =

flakes-ignore =
docs/*.py ALL

looponfailroots =
stl
tests
build
47 changes: 32 additions & 15 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,40 @@

setup_kwargs = {}


def error(*lines):
for line in lines:
print(line, file=sys.stderr)


try:
import numpy
from Cython import Build

setup_kwargs['ext_modules'] = Build.cythonize([
extension.Extension(
'stl._speedups',
['stl/_speedups.pyx'],
include_dirs=[numpy.get_include()],
),
])
from stl import stl
if not hasattr(stl, 'BaseStl'):
error('ERROR',
'You have an incompatible stl package installed'
'Please run "pip uninstall -y stl" first')
sys.exit(1)
except ImportError:
print('WARNING', file=sys.stderr)
print('Cython and Numpy is required for building extension.',
file=sys.stderr)
print('Falling back to pure Python implementation.', file=sys.stderr)
pass


if sys.version_info.major == 2 or sys.platform.lower() != 'win32':
try:
import numpy
from Cython import Build

setup_kwargs['ext_modules'] = Build.cythonize([
extension.Extension(
'stl._speedups',
['stl/_speedups.pyx'],
include_dirs=[numpy.get_include()],
),
])
except ImportError:
error('WARNING',
'Cython and Numpy is required for building extension.',
'Falling back to pure Python implementation.')


# To prevent importing about and thereby breaking the coverage info we use this
# exec hack
Expand All @@ -40,7 +58,6 @@

install_requires = [
'numpy',
'nine',
'python-utils>=1.6.2',
]

Expand Down
2 changes: 1 addition & 1 deletion stl/__about__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__package_name__ = 'numpy-stl'
__import_name__ = 'stl'
__version__ = '2.3.2'
__version__ = '2.4.0'
__author__ = 'Rick van Hattem'
__author_email__ = 'Wolph@Wol.ph'
__description__ = ' '.join('''
Expand Down
50 changes: 32 additions & 18 deletions stl/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,23 +106,23 @@ class BaseMesh(logger.Logged, collections.Mapping):
>>> # Check item 0 (contains v0, v1 and v2)
>>> mesh[0]
array([ 1., 1., 1., 2., 2., 2., 0., 0., 0.], dtype=float32)
>>> mesh.vectors[0] # doctest: +NORMALIZE_WHITESPACE
array([[ 1., 1., 1.],
[ 2., 2., 2.],
[ 0., 0., 0.]], dtype=float32)
array([1., 1., 1., 2., 2., 2., 0., 0., 0.], dtype=float32)
>>> mesh.vectors[0]
array([[1., 1., 1.],
[2., 2., 2.],
[0., 0., 0.]], dtype=float32)
>>> mesh.v0[0]
array([ 1., 1., 1.], dtype=float32)
array([1., 1., 1.], dtype=float32)
>>> mesh.points[0]
array([ 1., 1., 1., 2., 2., 2., 0., 0., 0.], dtype=float32)
>>> mesh.data[0] # doctest: +NORMALIZE_WHITESPACE
([ 0., 0., 0.], [[ 1., 1., 1.], [ 2., 2., 2.], [ 0., 0., 0.]], [0])
array([1., 1., 1., 2., 2., 2., 0., 0., 0.], dtype=float32)
>>> mesh.data[0]
([0., 0., 0.], [[1., 1., 1.], [2., 2., 2.], [0., 0., 0.]], [0])
>>> mesh.x[0]
array([ 1., 2., 0.], dtype=float32)
array([1., 2., 0.], dtype=float32)
>>> mesh[0] = 3
>>> mesh[0]
array([ 3., 3., 3., 3., 3., 3., 3., 3., 3.], dtype=float32)
array([3., 3., 3., 3., 3., 3., 3., 3., 3.], dtype=float32)
>>> len(mesh) == len(list(mesh))
True
Expand Down Expand Up @@ -315,6 +315,17 @@ def update_areas(self):
areas = .5 * numpy.sqrt((self.normals ** 2).sum(axis=1))
self.areas = areas.reshape((areas.size, 1))

def check(self):
if (self.normals.sum(axis=0) >= 1e-4).any():
self.warning('''
Your mesh is not closed, the mass methods will not function
correctly on this mesh. For more info:
https://github.com/WoLpH/numpy-stl/issues/69
'''.strip())
return False
else:
return True

def get_mass_properties(self):
'''
Evaluate and return a tuple with the following elements:
Expand All @@ -325,6 +336,8 @@ def get_mass_properties(self):
Documentation can be found here:
http://www.geometrictools.com/Documentation/PolyhedralMassProperties.pdf
'''
self.check()

def subexpression(x):
w0, w1, w2 = x[:, 0], x[:, 1], x[:, 2]
temp0 = w0 + w1
Expand Down Expand Up @@ -421,7 +434,7 @@ def rotation_matrix(cls, axis, theta):
[2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab)],
[2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc]])

def rotate(self, axis, theta, point=None):
def rotate(self, axis, theta=0, point=None):
'''
Rotate the matrix over the given axis by the given theta (angle)
Expand All @@ -442,6 +455,13 @@ def rotate(self, axis, theta, point=None):
if not theta:
return

self.rotate_using_matrix(self.rotation_matrix(axis, theta), point)

def rotate_using_matrix(self, rotation_matrix, point=None):
# No need to rotate if there is no actual rotation
if not rotation_matrix.any():
return

if isinstance(point, (numpy.ndarray, list, tuple)) and len(point) == 3:
point = numpy.asarray(point)
elif point is None:
Expand All @@ -451,12 +471,6 @@ def rotate(self, axis, theta, point=None):
else:
raise TypeError('Incorrect type for point', point)

rotation_matrix = self.rotation_matrix(axis, theta)

# No need to rotate if there is no actual rotation
if not rotation_matrix.any():
return

def _rotate(matrix):
if point.any():
# Translate while rotating
Expand Down
6 changes: 4 additions & 2 deletions stl/stl.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,9 @@ def get(prefix=''):

@classmethod
def _load_ascii(cls, fh, header, speedups=True):
if _speedups and speedups:
# The speedups module is covered by travis but it can't be tested in
# all environments, this makes coverage checks easier
if _speedups and speedups: # pragma: no cover
return _speedups.ascii_read(fh, header)
else:
iterator = cls._ascii_reader(fh, header)
Expand Down Expand Up @@ -259,7 +261,7 @@ def save(self, filename, fh=None, mode=AUTOMATIC, update_normals=True):
pass

def _write_ascii(self, fh, name):
if _speedups and self.speedups:
if _speedups and self.speedups: # pragma: no cover
_speedups.ascii_write(fh, b(name), self.data)
else:
def p(s, file):
Expand Down
15 changes: 15 additions & 0 deletions tests/stl_corruption.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from __future__ import print_function
import numpy
import pytest
import struct

Expand Down Expand Up @@ -127,3 +128,17 @@ def test_corrupt_binary_file(tmpdir, speedups):
fh.seek(0)
mesh.Mesh.from_file(str(tmp_file), fh=fh, speedups=speedups)


def test_duplicate_polygons():
data = numpy.zeros(3, dtype=mesh.Mesh.dtype)
data['vectors'][0] = numpy.array([[0, 0, 0],
[1, 0, 0],
[0, 1, 1.]])
data['vectors'][0] = numpy.array([[0, 0, 0],
[2, 0, 0],
[0, 2, 1.]])
data['vectors'][0] = numpy.array([[0, 0, 0],
[3, 0, 0],
[0, 3, 1.]])

assert not mesh.Mesh(data, remove_empty_areas=False).check()
21 changes: 21 additions & 0 deletions tests/test_rotate.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,27 @@ def test_rotation_over_point():
mesh.rotate([1, 0, 0], math.radians(180), point='x')


def test_double_rotation():
# 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)

rotation_matrix = mesh.rotation_matrix([1, 0, 0], math.radians(180))
combined_rotation_matrix = numpy.dot(rotation_matrix, rotation_matrix)

mesh.rotate_using_matrix(combined_rotation_matrix)
utils.array_equals(
mesh.vectors,
numpy.array([[[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]]]))


def test_no_rotation():
# Create a single face
data = numpy.zeros(1, dtype=Mesh.dtype)
Expand Down

0 comments on commit 443c8e5

Please sign in to comment.