Skip to content

Commit

Permalink
merge with main and bump to v0.2.1
Browse files Browse the repository at this point in the history
  • Loading branch information
akaszynski committed Jul 17, 2024
2 parents 92345ea + 54a5e0b commit d29aabf
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 29 deletions.
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ repos:
- id: debug-statements
- id: trailing-whitespace

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.1
hooks:
- id: mypy
additional_dependencies: [numpy>=2.0, npt-promote==0.1]


- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
rev: v2.14.0
hooks:
Expand Down
8 changes: 6 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@ classifiers = [
"Programming Language :: Python :: 3.12"
]
dependencies = [
"pyvista",
"numpy"
]
description = "Read in STL files"
name = "stl-reader"
readme = "README.rst"
requires-python = ">=3.8"
version = "0.2.0"
version = "0.2.1"

[tool.cibuildwheel]
archs = ["auto64"] # 64-bit only
Expand All @@ -43,6 +42,11 @@ MACOSX_DEPLOYMENT_TARGET = "10.14" # Needed for full C++17 support on MacOS
quiet-level = 3
skip = '*.cxx,*.h,*.gif,*.png,*.jpg,*.js,*.html,*.doctree,*.ttf,*.woff,*.woff2,*.eot,*.mp4,*.inv,*.pickle,*.ipynb,flycheck*,./.git/*,./.hypothesis/*,*.yml,./doc/build/*,./doc/images/*,./dist/*,*~,.hypothesis*,*.cpp,*.c'

[tool.mypy]
plugins = ["numpy.typing.mypy_plugin", 'npt_promote']
# disable_error_code = ['assignment', 'index', 'misc']
strict = true

[tool.pytest.ini_options]
filterwarnings = [
# bogus numpy ABI warning (see numpy/#432)
Expand Down
1 change: 1 addition & 0 deletions src/stl_reader/py.typed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
partial
34 changes: 28 additions & 6 deletions src/stl_reader/reader.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
"""Read a STL file using a wrapper of https://github.com/aki5/libstl."""

from typing import TYPE_CHECKING, Tuple

import numpy as np
import numpy.typing as npt

from stl_reader import stl_reader as _stlfile_wrapper

if TYPE_CHECKING:
from pyvista.core.pointset import PolyData


def _check_stl_ascii(filename: str) -> bool:
"""Check if a STL is ASCII."""
with open(filename, "rb") as fid:
return fid.read(5) == b"solid"

def _polydata_from_faces(points, faces):

def _polydata_from_faces(points: npt.NDArray[float], faces: npt.NDArray[int]) -> "PolyData":
"""Generate a polydata from a faces array containing no padding and all triangles.
This is a more efficient way of instantiating PolyData from point and face
Expand All @@ -20,7 +32,7 @@ def _polydata_from_faces(points, faces):
"""
try:
import pyvista as pv
from pyvista.core.pointset import PolyData
except ModuleNotFoundError:
raise ModuleNotFoundError(
"To use this functionality, install PyVista with:\n\npip install pyvista"
Expand All @@ -33,14 +45,14 @@ def _polydata_from_faces(points, faces):

# zero copy polydata creation
offset = np.arange(0, faces.size + 1, faces.shape[1], dtype=ID_TYPE)
pdata = pv.PolyData()
pdata = PolyData()
pdata.points = points
pdata.faces = CellArray.from_arrays(offset, faces)

return pdata


def read(filename):
def read(filename: str) -> Tuple[npt.NDArray[np.float32], npt.NDArray[np.uint32]]:
"""
Read a binary STL file and returns the vertices and points.
Expand Down Expand Up @@ -87,10 +99,13 @@ def read(filename):
[9005998, 9005999, 9005995]], dtype=uint32)
"""
if _check_stl_ascii(filename):
raise RuntimeError("stl-reader only supports binary STL files")

return _stlfile_wrapper.get_stl_data(filename)


def read_as_mesh(filename):
def read_as_mesh(filename: str) -> "PolyData":
"""
Read a binary STL file and return it as a mesh.
Expand Down Expand Up @@ -133,5 +148,12 @@ def read_as_mesh(filename):
Requires the ``pyvista`` library.
"""
try:
from pyvista import ID_TYPE
except ModuleNotFoundError:
raise ModuleNotFoundError(
"To use this functionality, install PyVista with:\n\npip install pyvista"
)
vertices, indices = read(filename)
return _polydata_from_faces(vertices, indices)
indices_int = indices.astype(ID_TYPE, copy=False)
return _polydata_from_faces(vertices, indices_int)
6 changes: 6 additions & 0 deletions src/stl_reader/stl_reader.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from typing import Tuple

import numpy as np
import numpy.typing as npt

def get_stl_data(filename: str) -> Tuple[npt.NDArray[np.float32], npt.NDArray[np.uint32]]: ...
142 changes: 142 additions & 0 deletions tests/sphere_ascii.stl
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
solid Visualization Toolkit generated SLA File
facet normal 0 0.93417234926898851 -0.35682211515159623
outer loop
vertex 0 0.52573114633560181 -0.85065090656280518
vertex -0.52573114633560181 0.85065090656280518 0
vertex 0.52573114633560181 0.85065090656280518 0
endloop
endfacet
facet normal 0 0.93417234926898851 0.35682211515159623
outer loop
vertex 0 0.52573114633560181 0.85065090656280518
vertex 0.52573114633560181 0.85065090656280518 0
vertex -0.52573114633560181 0.85065090656280518 0
endloop
endfacet
facet normal -0.35682211515159623 0 0.93417234926898851
outer loop
vertex 0 0.52573114633560181 0.85065090656280518
vertex -0.85065090656280518 0 0.52573114633560181
vertex 0 -0.52573114633560181 0.85065090656280518
endloop
endfacet
facet normal 0.35682211515159623 -0 0.93417234926898851
outer loop
vertex 0 0.52573114633560181 0.85065090656280518
vertex 0 -0.52573114633560181 0.85065090656280518
vertex 0.85065090656280518 0 0.52573114633560181
endloop
endfacet
facet normal 0.35682211515159623 0 -0.93417234926898851
outer loop
vertex 0 0.52573114633560181 -0.85065090656280518
vertex 0.85065090656280518 0 -0.52573114633560181
vertex 0 -0.52573114633560181 -0.85065090656280518
endloop
endfacet
facet normal -0.35682211515159623 0 -0.93417234926898851
outer loop
vertex 0 0.52573114633560181 -0.85065090656280518
vertex 0 -0.52573114633560181 -0.85065090656280518
vertex -0.85065090656280518 0 -0.52573114633560181
endloop
endfacet
facet normal 0 -0.93417234926898851 0.35682211515159623
outer loop
vertex 0 -0.52573114633560181 0.85065090656280518
vertex -0.52573114633560181 -0.85065090656280518 0
vertex 0.52573114633560181 -0.85065090656280518 0
endloop
endfacet
facet normal -0 -0.93417234926898851 -0.35682211515159623
outer loop
vertex 0 -0.52573114633560181 -0.85065090656280518
vertex 0.52573114633560181 -0.85065090656280518 0
vertex -0.52573114633560181 -0.85065090656280518 0
endloop
endfacet
facet normal -0.93417234926898851 0.35682211515159623 0
outer loop
vertex -0.52573114633560181 0.85065090656280518 0
vertex -0.85065090656280518 0 -0.52573114633560181
vertex -0.85065090656280518 0 0.52573114633560181
endloop
endfacet
facet normal -0.93417234926898851 -0.35682211515159623 -0
outer loop
vertex -0.52573114633560181 -0.85065090656280518 0
vertex -0.85065090656280518 0 0.52573114633560181
vertex -0.85065090656280518 0 -0.52573114633560181
endloop
endfacet
facet normal 0.93417234926898851 0.35682211515159623 0
outer loop
vertex 0.52573114633560181 0.85065090656280518 0
vertex 0.85065090656280518 0 0.52573114633560181
vertex 0.85065090656280518 0 -0.52573114633560181
endloop
endfacet
facet normal 0.93417234926898851 -0.35682211515159623 0
outer loop
vertex 0.52573114633560181 -0.85065090656280518 0
vertex 0.85065090656280518 0 -0.52573114633560181
vertex 0.85065090656280518 0 0.52573114633560181
endloop
endfacet
facet normal -0.57735026918962573 0.57735026918962573 0.57735026918962573
outer loop
vertex 0 0.52573114633560181 0.85065090656280518
vertex -0.52573114633560181 0.85065090656280518 0
vertex -0.85065090656280518 0 0.52573114633560181
endloop
endfacet
facet normal 0.57735026918962573 0.57735026918962573 0.57735026918962573
outer loop
vertex 0 0.52573114633560181 0.85065090656280518
vertex 0.85065090656280518 0 0.52573114633560181
vertex 0.52573114633560181 0.85065090656280518 0
endloop
endfacet
facet normal -0.57735026918962573 0.57735026918962573 -0.57735026918962573
outer loop
vertex 0 0.52573114633560181 -0.85065090656280518
vertex -0.85065090656280518 0 -0.52573114633560181
vertex -0.52573114633560181 0.85065090656280518 0
endloop
endfacet
facet normal 0.57735026918962573 0.57735026918962573 -0.57735026918962573
outer loop
vertex 0 0.52573114633560181 -0.85065090656280518
vertex 0.52573114633560181 0.85065090656280518 0
vertex 0.85065090656280518 0 -0.52573114633560181
endloop
endfacet
facet normal -0.57735026918962573 -0.57735026918962573 -0.57735026918962573
outer loop
vertex 0 -0.52573114633560181 -0.85065090656280518
vertex -0.52573114633560181 -0.85065090656280518 0
vertex -0.85065090656280518 0 -0.52573114633560181
endloop
endfacet
facet normal 0.57735026918962573 -0.57735026918962573 -0.57735026918962573
outer loop
vertex 0 -0.52573114633560181 -0.85065090656280518
vertex 0.85065090656280518 0 -0.52573114633560181
vertex 0.52573114633560181 -0.85065090656280518 0
endloop
endfacet
facet normal -0.57735026918962573 -0.57735026918962573 0.57735026918962573
outer loop
vertex 0 -0.52573114633560181 0.85065090656280518
vertex -0.85065090656280518 0 0.52573114633560181
vertex -0.52573114633560181 -0.85065090656280518 0
endloop
endfacet
facet normal 0.57735026918962573 -0.57735026918962573 0.57735026918962573
outer loop
vertex 0 -0.52573114633560181 0.85065090656280518
vertex 0.52573114633560181 -0.85065090656280518 0
vertex 0.85065090656280518 0 0.52573114633560181
endloop
endfacet
endsolid
Binary file added tests/sphere_binary.stl
Binary file not shown.
85 changes: 64 additions & 21 deletions tests/test_reader.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,83 @@
"""Test stl_reader."""

import os
import numpy as np
import pytest
import pyvista as pv

import stl_reader


@pytest.fixture
def stlfile(tmpdir):
filename = tmpdir.join("tmp.stl")
pv.Sphere().save(filename)
return str(filename)
try:
import pyvista as pv

PYVISTA_INSTALLED = True
except ImportError:
PYVISTA_INSTALLED = False

THIS_PATH = os.path.dirname(os.path.abspath(__file__))
TEST_FILE_ASCII = os.path.join(THIS_PATH, "sphere_ascii.stl")
TEST_FILE_BINARY = os.path.join(THIS_PATH, "sphere_binary.stl")

EXPECTED_POINTS = np.array(
[
[0.0, 0.52573115, -0.8506509],
[-0.52573115, 0.8506509, 0.0],
[0.52573115, 0.8506509, 0.0],
[0.0, 0.52573115, 0.8506509],
[-0.8506509, 0.0, 0.52573115],
[0.0, -0.52573115, 0.8506509],
[0.8506509, 0.0, 0.52573115],
[0.8506509, 0.0, -0.52573115],
[0.0, -0.52573115, -0.8506509],
[-0.8506509, 0.0, -0.52573115],
[-0.52573115, -0.8506509, 0.0],
[0.52573115, -0.8506509, 0.0],
],
dtype=np.float32,
)

@pytest.fixture
def stlfile_ascii(tmpdir):
filename = tmpdir.join("tmp.stl")
pv.Sphere().save(filename, binary=False)
return str(filename)

EXPECTED_FACES = np.array(
[
[0, 1, 2],
[3, 2, 1],
[3, 4, 5],
[3, 5, 6],
[0, 7, 8],
[0, 8, 9],
[5, 10, 11],
[8, 11, 10],
[1, 9, 4],
[10, 4, 9],
[2, 6, 7],
[11, 7, 6],
[3, 1, 4],
[3, 6, 2],
[0, 9, 1],
[0, 2, 7],
[8, 10, 9],
[8, 7, 11],
[5, 4, 10],
[5, 11, 6],
],
dtype=np.uint32,
)

def test_read_binary(stlfile):
pv_mesh = pv.read(stlfile)

points, ind = stl_reader.read(stlfile)
assert np.allclose(pv_mesh.points, points)
assert np.allclose(pv_mesh._connectivity_array, ind.ravel())
def test_read_binary() -> None:
points, ind = stl_reader.read(TEST_FILE_BINARY)
assert np.allclose(EXPECTED_POINTS, points)
assert np.allclose(EXPECTED_FACES, ind)


def test_read_ascii(stlfile_ascii):
def test_read_ascii() -> None:
with pytest.raises(RuntimeError):
stl_reader.read(stlfile_ascii)
stl_reader.read(TEST_FILE_ASCII)


def test_read_as_mesh(stlfile):
pv_mesh = pv.read(stlfile)
@pytest.mark.skipif(not PYVISTA_INSTALLED, reason="Requires PyVista") # type: ignore
def test_read_as_mesh() -> None:
pv_mesh = pv.read(TEST_FILE_BINARY)

stl_mesh = stl_reader.read_as_mesh(stlfile)
stl_mesh = stl_reader.read_as_mesh(TEST_FILE_BINARY)
assert pv_mesh == stl_mesh

0 comments on commit d29aabf

Please sign in to comment.