Skip to content

Commit

Permalink
Merge branch 'release/2.11.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
wolph committed Mar 24, 2020
2 parents dcbe8cd + 833bf29 commit 99948d0
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 52 deletions.
6 changes: 5 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
dist: xenial
sudo: false
language: python
python: 3.6
Expand All @@ -12,7 +13,10 @@ env:
- TOX_ENV=py27-nix
- TOX_ENV=py34-nix
- TOX_ENV=py35-nix
- TOX_ENV=py36-nix
# - TOX_ENV=py36-nix
- TOX_ENV=py37-nix
- TOX_ENV=py38-nix
- TOX_ENV=py39-nix
matrix:
include:
- env: TOX_ENV=py37-nix
Expand Down
23 changes: 6 additions & 17 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -336,23 +336,12 @@ Combining multiple STL files
# 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 = maxx = miny = maxy = minz = maxz = None
for p in obj.points:
# p contains (x, y, z)
if minx is None:
minx = p[stl.Dimension.X]
maxx = p[stl.Dimension.X]
miny = p[stl.Dimension.Y]
maxy = p[stl.Dimension.Y]
minz = p[stl.Dimension.Z]
maxz = p[stl.Dimension.Z]
else:
maxx = max(p[stl.Dimension.X], maxx)
minx = min(p[stl.Dimension.X], minx)
maxy = max(p[stl.Dimension.Y], maxy)
miny = min(p[stl.Dimension.Y], miny)
maxz = max(p[stl.Dimension.Z], maxz)
minz = min(p[stl.Dimension.Z], minz)
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
Expand Down
23 changes: 16 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import warnings
from setuptools import setup, extension
from setuptools.command.build_ext import build_ext
from setuptools.command.test import test as TestCommand

setup_kwargs = {}

Expand All @@ -25,6 +26,19 @@ def error(*lines):
pass


class PyTest(TestCommand):
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True

def run_tests(self):
# import here, cause outside the eggs aren't loaded
import pytest
errno = pytest.main(self.test_args)
sys.exit(errno)


if sys.version_info.major == 2 or sys.platform.lower() != 'win32':
try:
import numpy
Expand Down Expand Up @@ -68,12 +82,7 @@ def error(*lines):
install_requires.append('enum34')


if os.environ.get('PYTEST_RUNNER', '').lower() == 'false':
tests_require = []
setup_requires = []
else:
tests_require = ['pytest']
setup_requires = ['pytest-runner']
tests_require = ['pytest']


class BuildExt(build_ext):
Expand Down Expand Up @@ -101,7 +110,6 @@ def run(self):
packages=['stl'],
long_description=long_description,
tests_require=tests_require,
setup_requires=setup_requires,
entry_points={
'console_scripts': [
'stl = %s.main:main' % about['__import_name__'],
Expand All @@ -128,6 +136,7 @@ def run(self):
install_requires=install_requires,
cmdclass=dict(
build_ext=BuildExt,
test=PyTest,
),
**setup_kwargs
)
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.10.1'
__version__ = '2.11.0'
__author__ = 'Rick van Hattem'
__author_email__ = 'Wolph@Wol.ph'
__description__ = ' '.join('''
Expand Down
18 changes: 12 additions & 6 deletions stl/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,9 +316,10 @@ def remove_empty_areas(cls, data):
def update_normals(self):
'''Update the normals for all points'''
normals = numpy.cross(self.v1 - self.v0, self.v2 - self.v0)
normal = numpy.linalg.norm(normals)
if normal:
normals /= normal
normal = numpy.linalg.norm(normals, axis=1)
non_zero = normal > 0
if non_zero.any():
normals[non_zero] /= normal[non_zero][:, None]
self.normals[:] = normals

def update_min(self):
Expand All @@ -335,7 +336,7 @@ def check(self):
'''Check the mesh is valid or not'''
return self.is_closed()

def is_closed(self):
def is_closed(self): # pragma: no cover
"""Check the mesh is closed or not"""
if (self.normals.sum(axis=0) >= 1e-4).any():
self.warning('''
Expand Down Expand Up @@ -436,7 +437,7 @@ def rotation_matrix(cls, axis, theta):
axis = numpy.asarray(axis)
# No need to rotate if there is no actual rotation
if not axis.any():
return numpy.zeros((3, 3))
return numpy.identity(3)

theta = 0.5 * numpy.asarray(theta)

Expand Down Expand Up @@ -479,8 +480,9 @@ def rotate(self, axis, theta=0, point=None):
self.rotate_using_matrix(self.rotation_matrix(axis, theta), point)

def rotate_using_matrix(self, rotation_matrix, point=None):
identity = numpy.identity(rotation_matrix.shape[0])
# No need to rotate if there is no actual rotation
if not rotation_matrix.any():
if not rotation_matrix.any() or (identity == rotation_matrix).all():
return

if isinstance(point, (numpy.ndarray, list, tuple)) and len(point) == 3:
Expand All @@ -500,6 +502,10 @@ def _rotate(matrix):
# Simply apply the rotation
return matrix.dot(rotation_matrix)

# Rotate the normals
self.normals[:] = _rotate(self.normals[:])

# Rotate the vectors
for i in range(3):
self.vectors[:, i] = _rotate(self.vectors[:, i])

Expand Down
2 changes: 1 addition & 1 deletion stl/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def to_ascii():


def to_binary():
parser = _get_parser('Convert STL files to ASCII (text) format')
parser = _get_parser('Convert STL files to binary format')
args = parser.parse_args()
name = _get_name(args)
stl_file = stl.StlMesh(filename=name, fh=args.infile,
Expand Down
37 changes: 22 additions & 15 deletions stl/stl.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class Mode(enum.IntEnum):
COUNT_SIZE = 4
#: The maximum amount of triangles we can read from binary files
MAX_COUNT = 1e8
#: The header format, can be safely monkeypatched. Limited to 80 characters
HEADER_FORMAT = '{package_name} ({version}) {now} {name}'


class BaseStl(base.BaseMesh):
Expand All @@ -56,7 +58,7 @@ def load(cls, fh, mode=AUTOMATIC, speedups=True):
:param file fh: The file handle to open
:param int mode: Automatically detect the filetype or force binary
'''
header = fh.read(HEADER_SIZE).lower()
header = fh.read(HEADER_SIZE)
if not header:
return

Expand All @@ -65,7 +67,7 @@ def load(cls, fh, mode=AUTOMATIC, speedups=True):

name = ''

if mode in (AUTOMATIC, ASCII) and header.startswith(b('solid')):
if mode in (AUTOMATIC, ASCII) and header[:5].lower() == b('solid'):
try:
name, data = cls._load_ascii(
fh, header, speedups=speedups)
Expand Down Expand Up @@ -136,20 +138,22 @@ def _ascii_reader(cls, fh, header):
lines = b(header).split(b('\n'))

def get(prefix=''):
prefix = b(prefix)
prefix = b(prefix).lower()

if lines:
line = lines.pop(0)
raw_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)
raw_line += lines.pop(0)

line = line.lower().strip()
raw_line = raw_line.strip()
line = raw_line.lower()
if line == b(''):
return get(prefix)

Expand All @@ -174,7 +178,7 @@ def get(prefix=''):
raise RuntimeError(recoverable[0],
'Incorrect value %r' % line)
else:
return b(line)
return b(raw_line)

line = get()
if not lines:
Expand Down Expand Up @@ -275,17 +279,20 @@ def p(s, file):

p('endsolid %s' % name, file=fh)

def _write_binary(self, fh, name):
# Create the header
header = '%s (%s) %s %s' % (
metadata.__package_name__,
metadata.__version__,
datetime.datetime.now(),
name,
def get_header(self, name):
# Format the header
header = HEADER_FORMAT.format(
package_name=metadata.__package_name__,
version=metadata.__version__,
now=datetime.datetime.now(),
name=name,
)

# Make it exactly 80 characters
header = header[:80].ljust(80, ' ')
return header[:80].ljust(80, ' ')

def _write_binary(self, fh, name):
header = self.get_header(name)
packed = struct.pack(s('<i'), self.data.size)

if isinstance(fh, io.TextIOWrapper): # pragma: no cover
Expand Down
6 changes: 3 additions & 3 deletions tests/test_mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ def test_units_2d():
mesh = Mesh(data, remove_empty_areas=False)
mesh.update_units()

assert numpy.allclose(mesh.areas, [0.35355338, 0.35355338])
assert numpy.allclose(mesh.areas, [0.5, 0.5])
assert numpy.allclose(mesh.normals, [
[0.0, 0.0, 0.70710677],
[0.0, 0.0, -0.70710677]])
[0.0, 0.0, 1.0],
[0.0, 0.0, -1.0]])
assert numpy.allclose(mesh.units, [[0, 0, 1], [0, 0, -1]])


Expand Down
8 changes: 7 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = {py27,py33,py34,py35,py36,py37}-{windows-32,windows-64,nix}, docs, flake8
envlist = {py27,py33,py34,py35,py36,py37,py38,py39}-{windows-32,windows-64,nix}, docs, flake8
skip_missing_interpreters = True

[testenv]
Expand All @@ -13,6 +13,8 @@ basepython =
py35-nix: python3.5
py36-nix: python3.6
py37-nix: python3.7
py38-nix: python3.7
py39-nix: python3.7
py27-windows-32: C:\\Python27\\python.exe
py27-windows-64: C:\\Python27-x64\\python.exe
py34-windows-32: C:\\Python34\\python.exe
Expand All @@ -23,6 +25,10 @@ basepython =
py36-windows-64: C:\\Python36-x64\\python.exe
py37-windows-32: C:\\Python37\\python.exe
py37-windows-64: C:\\Python37-x64\\python.exe
py38-windows-32: C:\\Python38\\python.exe
py38-windows-64: C:\\Python38-x64\\python.exe
py39-windows-32: C:\\Python39\\python.exe
py39-windows-64: C:\\Python39-x64\\python.exe

[testenv:flake8]
basepython=python
Expand Down

0 comments on commit 99948d0

Please sign in to comment.