Science Score: 26.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
○CITATION.cff file
-
✓codemeta.json file
Found codemeta.json file -
✓.zenodo.json file
Found .zenodo.json file -
○DOI references
-
○Academic publication links
-
○Committers with academic emails
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (9.7%) to scientific vocabulary
Repository
A sophisticated mesh class for analysing 3D surfaces.
Basic Info
- Host: GitHub
- Owner: bwoodsend
- License: mit
- Language: Python
- Default Branch: main
- Homepage: https://bwoodsend.github.io/motmot
- Size: 5.97 MB
Statistics
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
- Releases: 4
Metadata Files
README.md
Motmot
A sophisticated mesh class for analysing colourless Polygon meshes such as an STL file providing a seamless abstraction between raw vectors meshes or the more efficient vertices + faces (a.k.a vertices + polygons) form.
- Free software: MIT license
- Source code: https://github.com/bwoodsend/motmot/
- Releases: https://pypi.org/project/motmot/
- Documentation: https://bwoodsend.github.io/motmot/index.html
Related projects
This library focuses almost exclusively on analysing meshes. It it highly likely that you will need to supplement it with other libraries to read/write to a certain format or to simplify an existing mesh.
Mesh read/write:
Mesh analysis:
- PyMesh: A highly sophisticated mesh library which unfortunately depends on some rather hairy C++ libraries, making it not generally installable. It's not even on PyPI.
- trimesh:
Another general purpose mesh library. This one is pure Python and focuses
strictly on triangular and preferably closed meshes.
It also brings a few readers and writers with it.
This library is very powerful.
It's quite likely that you'd be better off using it instead of
motmot.
Mesh cleaning:
- quadmeshsimplify: Decimate (collapse redundant or near redundant vertices in) meshes to make the filesize much smaller with negligible impact on quality.
- Py_Fast-Quadric-Mesh-Simplification: Another mesh decimator. This one is much faster but not on PyPI (yet).
Usage
The basic API for motmot is modelled off that of numpy-stl with a few
alterations.
Initialisation
Meshes can be :
- Constructed from scratch using a single vectors array.
This array should be 3D with shape
(n, k, 3)where:
* ``n`` is the number of polygons in the mesh,
* ``k`` is the number of corners each polygon has,
* ``3`` corresponds to having 3 axes. i.e. ``x``, ``y`` and ``z``.
```python
# vectors is a (n, 3, 3) numpy array.
triangle_mesh = Mesh(vectors)
# vectors is a (n, 4, 3) numpy array.
square_mesh = Mesh(vectors)
```
Or using the more memory efficient vertices + faces form.
```python
verticesis an array of points. It should contain no duplicates.facesis an integer array indicating which vertices are used to constructeach polygon.
mesh = Mesh(vertices, faces) ```
Read from an STL file. This uses numpy-stl under the hood. Currently, STL is the only file format that
motmotwill read implicitly:python from motmot import Mesh mesh = Mesh("some-file.stl")Read from an lzma, gzip or bzip2 compressed STL file:
```python from motmot import Mesh
An lzma compressed STL file. Create using
xz some-file.stlin bash.mesh = Mesh("some-file.stl.xz")
A gzip compressed STL file. Create using
gzip some-file.stlin bash.mesh = Mesh("some-file.stl.gz")
A bzip2 compressed STL file. Create using
bzip2 some-file.stlin bash.mesh = Mesh("some-file.stl.bz2") ```
Stream from any subclass of
io.RawIOBase. From here you can read from arbitrary sources such as embedded files, streams, archives or other pseudo files. For example, the following reads an STL directly from a web request:```python from urllib import request from motmot import Mesh
Pull an STL file from the internet and load it without an intermediate
temporary file.
url = "https://raw.githubusercontent.com/bwoodsend/vtkplotlib/master/" \ "vtkplotlib/data/models/rabbit/rabbit.stl"
with request.urlopen(url) as req: mesh = Mesh(req) ```
Vertices + Faces meshes vs Vectors meshes
There are two forms of mesh.
A vectors mesh is essentially a list of polygons where each polygon is a list of points (its corners) and each point is an
(x, y, z)triplet. This form is simple but wasteful because points which appear in multiple polygons are written multiple times which wastes memory and rendering time.A vertices+faces mesh takes all the unique points from a vectors mesh, calling them the vertices, then replaces each point in vectors with its index from vertices, calling this faces. Note that faces is often also known as facets or polygons.
Motmot makes the two forms interchangeable. Each of vectors, vertices and faces are all available as attributes on all meshes but, depending on how a mesh is constructed, vectors may be internally derived from vertices and faces or vice-versa.
```python import numpy as np from motmot import Mesh
Define the 8 vertices in a cuboid.
vertices = np.array([ [0., 0., 0.], [3., 0., 0.], [0., 5., 0.], [3., 5., 0.], [0., 0., 9.], [3., 0., 9.], [0., 5., 9.], [3., 5., 9.], ])
Define the 6 sides of a cube or cuboid.
faces = np.array([ # Draw a square using vertices[6], vertices[2], vertices[0] and vertices[4] [6, 2, 0, 4], # Draw a square using vertices[0], vertices[1], vertices[5] and vertices[4] [0, 1, 5, 4], # And so on... [0, 2, 3, 1], [5, 1, 3, 7], [3, 2, 6, 7], [4, 5, 7, 6], ])
Construct a vertices+faces mesh.
mesh = Mesh(vertices, faces)
This attribute is set to True to signify that this was originally a faces mesh.
mesh.isfacesmesh
Although vectors can still be derived automatically.
mesh.vectors
Construct a vectors mesh.
mesh = Mesh(vertices[faces])
This attribute is set to False to signify that this was originally a vectors
mesh.
mesh.isfacesmesh
But vertices and faces can still be derived automatically.
mesh.vertices, mesh.faces ```
Mesh properties
This is just a brief summary of what is available. See the corresponding entry in the the API reference for more information on each property.
```python
Outward normal to each polygon:
mesh.normals array([[-45., 0., 0.], [ -0., -27., -0.], [ -0., -0., -15.], [ 45., 0., -0.], [ -0., 27., 0.], [ 0., 0., 15.]])
Normalised (magnitude of 1.0) outward normal to each polygon:
mesh.units array([[-1., 0., 0.], [ 0., -1., 0.], [ 0., 0., -1.], [ 1., 0., 0.], [ 0., 1., 0.], [ 0., 0., 1.]])
Area of each polygon.
mesh.areas array([45., 27., 15., 45., 27., 15.])
Total surface area (just a sum of mesh.areas).
mesh.area 174.0
The number of times each vertex is used (which admittedly
isn't particularly interesting for a cuboid):
mesh.vertex_counts array([3, 3, 3, 3, 3, 3, 3, 3], dtype=int32)
A mapping of which other vertices each vertex is directly connect to.
mesh.vertexmap RaggedArray.fromnested([ [1, 7, 3], # vertices[0] connects to vertices[[1, 7, 3]]. [2, 6, 0], # vertices[1] connects to vertices[[2, 6, 0]]. [4, 1, 3], # and so on... [5, 0, 2], [5, 6, 2], [4, 7, 3], [1, 4, 7], [0, 5, 6], ])
Because this mesh's vertices appear the same number of times,
this example slightly trivialises the problem. Consider instead
a mesh with only the first three faces. Not all vertices have
the same number of neighbours.
mesh[:3].vertexmap RaggedArray.fromnested([ [1, 3], [2, 6, 0], [4, 1, 3], [0, 2, 5], [5, 2, 6], [3, 4], [4, 1], ])
If you prefer to use raw vertices rather than vertex IDs then
use the connected_vertices() method.
mesh.connected_vertices(mesh.vertices[0]) array([[0., 5., 0.], [3., 5., 9.], [0., 0., 9.]])
Similarly, polygon_map maps every polygon to each of its neighbours.
Read the first line of the following as *polygon 0 shares an edge each with
polygons 4, 2, 1 and 5*.
mesh.polygon_map array([[4, 2, 1, 5], [2, 3, 5, 0], [0, 4, 3, 1], [1, 2, 4, 5], [2, 0, 5, 3], [1, 3, 4, 0]]) ```
Vertex Lookup
motmot leverages two libraries for looking up vertices.
Exact lookup
It is easy to convert vertex IDs to real vertices.
Simply pass them as indices to mesh.vertices.
```python
ids = [0, 4, 5, 2] points = mesh.vertices[ids] points array([[0., 0., 0.], [0., 0., 9.], [3., 0., 9.], [0., 5., 0.]]) ```
Go the other way by indexing the vertex_table attribute.
```python
mesh.vertex_table[points] array([0, 4, 5, 2], dtype=int64) ```
Some things to be aware of:
The
dtypeof the points queried must matchmesh.dtype.As with regular floats in a regular Python
dict, even the smallest deviation will cause lookup to fail.```python
mesh.vertextable[[3., 0., 9.]] 5 mesh.vertextable[[3., 0, 9.00000000001]] KeyError: 'key = array([3., 0., 9.]) is not in this table.' ```
Approximate lookup
To find nearest points, motmot uses a KDTree.
The API here is very shallow and it is quite likely that you may wish to
create and use KDTrees directly rather than use motmot's methods.
A KDTree, fitted to mesh.centers (the middle of each polygon),
is found at the mesh.kdtree attribute.
Given a set of points defined as:
python
points = np.array([[2., 3.5, 4.2], [2.3, 4.2, 1.1]], mesh.dtype)
Find the nearest point on the mesh surface to each point:
```python
mesh.closest_point(points) array([[3. , 3.5, 4.2], [2.3, 4.2, 0. ]]) ``
Or to restrict the output to onlymesh.centers` without interpolating between them:
```python
mesh.closest_point(points, interpolate=False) array([[3. , 2.5, 4.5], [1.5, 2.5, 0. ]]) ```
For anything else, use mesh.kdtree directly.
Laziness
A motmot.Mesh lazy loads its properties using a backport of
@functools.cached_property.
This allows them to be calculated when only you need them so that no time is
ever wasted calculating something which you do not use.
Take for example, mesh.normals.
Nothing is calculated on
mesh = Mesh(vertices, faces) so that if the normals are never used then they are
never calculated.
Accessing the attribute mesh.normals initialises and returns
them making mesh.normals look like a regular attribute on the outside.
The value is cached so that the calculation never runs more than once.
i.e. mesh.normals is mesh.normals.
Caches should be reset after a mesh is modified.
Most of this is done automatically.
Mesh modifier methods such as rotate(), translate() or crop(in_place=True)
will all invalidate affected caches themselves.
Similarly, setting any of the vertices, faces or vectors attributes will
reset all caches.
Writing in place to those arrays (e.g. mesh.vectors[:] = x) however
is undetectable to motmot.
Call mesh.reset() after doing an in place modification.
Owner
- Name: Brénainn Woodsend
- Login: bwoodsend
- Kind: user
- Location: Scotland
- Repositories: 11
- Profile: https://github.com/bwoodsend
Software engineer and/or maths graduate. Specialises in Python with some C/C++. Currently mixing AI into dentistry. Name is pronounced Bren-an Woods-end.
GitHub Events
Total
- Delete event: 1
- Push event: 3
- Create event: 1
Last Year
- Delete event: 1
- Push event: 3
- Create event: 1
Committers
Last synced: almost 3 years ago
Top Committers
| Name | Commits | |
|---|---|---|
| bwoodsend | b****d@g****m | 99 |
Issues and Pull Requests
Last synced: 12 months ago
All Time
- Total issues: 0
- Total pull requests: 0
- Average time to close issues: N/A
- Average time to close pull requests: N/A
- Total issue authors: 0
- Total pull request authors: 0
- Average comments per issue: 0
- Average comments per pull request: 0
- Merged pull requests: 0
- Bot issues: 0
- Bot pull requests: 0
Past Year
- Issues: 0
- Pull requests: 0
- Average time to close issues: N/A
- Average time to close pull requests: N/A
- Issue authors: 0
- Pull request authors: 0
- Average comments per issue: 0
- Average comments per pull request: 0
- Merged pull requests: 0
- Bot issues: 0
- Bot pull requests: 0
Top Authors
Issue Authors
Pull Request Authors
Top Labels
Issue Labels
Pull Request Labels
Packages
- Total packages: 1
-
Total downloads:
- pypi 107 last-month
- Total dependent packages: 0
- Total dependent repositories: 1
- Total versions: 5
- Total maintainers: 1
pypi.org: motmot
A sophisticated mesh class for analysing 3D surfaces.
- Homepage: https://github.com/bwoodsend/motmot
- Documentation: https://motmot.readthedocs.io/
- License: MIT license
-
Latest release: 0.3.2
published almost 4 years ago
Rankings
Maintainers (1)
Dependencies
- actions/checkout v4 composite
- actions/setup-python v5 composite
- actions/upload-artifact v3 composite
- bwoodsend/setup-winlibs-action v1 composite
- actions/checkout v4 composite
- actions/setup-python v5 composite
- peaceiris/actions-gh-pages v3 composite
- actions/checkout v4 composite
- actions/setup-python v5 composite
- bwoodsend/setup-winlibs-action v1 composite
- quay.io/pypa/${BASE} latest build
- Sphinx ==4.4.0
- docutils ==0.16
- m2r2 ==0.3.2
- sphinx-autodoc-typehints ==1.11.1
- sphinx-copybutton ==0.3.3
- sphinx-rtd-theme ==0.5.0
- sphinxcontrib-applehelp ==1.0.2
- sphinxcontrib-devhelp ==1.0.2
- sphinxcontrib-htmlhelp ==2.0.0
- sphinxcontrib-jsmath ==1.0.1
- sphinxcontrib-qthelp ==1.0.3
- sphinxcontrib-serializinghtml ==1.1.5
- sphinxcontrib-websupport ==1.1.0