Metadata-Version: 2.1
Name: pyvista-miniply
Version: 0.3.0
Summary: Rapidly read in PLY files using a wrapper over miniply
Author-Email: PyVista Developers <info@pyvista.org>
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: >=3.10
Requires-Dist: numpy
Provides-Extra: pyvista
Requires-Dist: pyvista; extra == "pyvista"
Description-Content-Type: text/x-rst

#################
 pyvista-miniply
#################

|pypi| |MIT|

.. |pypi| image:: https://img.shields.io/pypi/v/pyvista-miniply.svg?logo=python&logoColor=white
   :target: https://pypi.org/project/pyvista-miniply/

.. |MIT| image:: https://img.shields.io/badge/License-MIT-yellow.svg
   :target: https://opensource.org/licenses/MIT

``pyvista-miniply`` is a Python library for rapidly reading PLY files.
It is a Python wrapper around the fast C++ PLY reading library provided
by `miniply <https://github.com/vilya/miniply>`_. Thanks @vilya!

The main advantage of ``pyvista-miniply`` over other PLY reading
libraries is its performance. See the benchmarks below for more details.

**************
 Installation
**************

The recommended way to install ``pyvista-miniply`` is via PyPI:

.. code:: sh

   pip install pyvista-miniply

Optionally with PyVista:

.. code:: sh

   pip install pyvista-miniply[pyvista]

You can also clone the repository and install it from source:

.. code:: sh

   git clone https://github.com/pyvista/pyvista-miniply.git
   cd pyvista-miniply
   git submodule update --init --recursive
   pip install .

*******
 Usage
*******

Load in the vertices, indices, normals, UV, and color information from a
PLY file:

.. code:: pycon

   >>> import pyvista_miniply
   >>> vertices, indices, normals, uv, color = pyvista_miniply.read("example.ply")
   >>> vertices
   array([[ 5.0000000e-01, -5.0000000e-01, -5.5511151e-17],
          [ 4.0000001e-01, -5.0000000e-01, -4.4408922e-17],
          [ 3.0000001e-01, -5.0000000e-01, -3.3306692e-17],
          ...,
          [-4.2500001e-01,  5.0000000e-01,  4.7184480e-17],
          [-4.7499999e-01,  4.4999999e-01,  5.2735593e-17],
          [-5.0000000e-01,  4.2500001e-01,  5.5511151e-17]], dtype=float32)
   >>> indices
   array([[   0,  442,  441],
          [ 442,  122,  443],
          [ 443,  121,  441],
          ...,
          [1677,  438, 1679],
          [1679,  439, 1676],
          [1677, 1679, 1676]], dtype=int32)
   >>> normals
   array([[-1.110223e-16,  0.000000e+00, -1.000000e+00],
          [-1.110223e-16,  0.000000e+00, -1.000000e+00],
          [-1.110223e-16,  0.000000e+00, -1.000000e+00],
          ...,
          [-1.110223e-16,  0.000000e+00, -1.000000e+00],
          [-1.110223e-16,  0.000000e+00, -1.000000e+00],
          [-1.110223e-16,  0.000000e+00, -1.000000e+00]], dtype=float32)
   >>> uv
   array([[0.        , 0.        ],
          [0.1       , 0.        ],
          [0.2       , 0.        ],
          ...,
          [0.92499995, 1.        ],
          [0.975     , 0.95      ],
          [1.        , 0.92499995]], dtype=float32)
   >>> color
   array([[  0,   0,   0],
          [  0,   0,   0],
          [  0,   0,   0],
          ...,
          [254, 254, 254],
          [254, 254, 254],
          [255, 255, 255]], dtype=uint8)

You can also read in the PLY file as a `PyVista
<https://github.com/pyvista>`_ PolyData and immediately plot it.

.. code:: pycon

    >>> import pyvista_miniply
    >>> mesh = pyvista_miniply.read_as_mesh("example.ply")
    >>> mesh
    PolyData (0x7f0653579c00)
      N Cells:    200
      N Points:   121
      N Strips:   0
      X Bounds:   -5.000e-01, 5.000e-01
      Y Bounds:   -5.000e-01, 5.000e-01
      Z Bounds:   -5.551e-17, 5.551e-17
      N Arrays:   2

   >>> mesh.plot()

.. image:: https://github.com/pyvista/pyvista-miniply/raw/main/demo.png

When ``pyvista-miniply`` is installed alongside ``pyvista >= 0.48``,
``pyvista.read`` automatically dispatches ``.ply`` files through
``pyvista-miniply`` via the ``pyvista.readers`` entry point, no manual
registration is required:

.. code:: pycon

   >>> import pyvista as pv
   >>> mesh = pv.read("example.ply")  # now powered by pyvista-miniply

***********
 Benchmark
***********

The main reason behind writing yet another PLY file reader for Python is
to leverage the highly performant ``miniply`` library.

There is already a benchmark demonstrating how ``miniply`` outperforms
in comparison to competing C and C++ libraries at `ply_io_benchmark
<https://github.com/mhalber/ply_io_benchmark>`_ when reading PLY files.
The benchmark here shows how ``pyvista-miniply`` performs relative to
other Python PLY file readers.

Here are the timings from reading in a 1,000,000 point binary PLY file
on an Intel i9-14900KF:

+-------------------+-----------------+
| Library           | Time (seconds)  |
+===================+=================+
| pyvista-miniply   | 0.027           |
+-------------------+-----------------+
| open3d            | 0.102           |
+-------------------+-----------------+
| PyVista (VTK)     | 0.214           |
+-------------------+-----------------+
| meshio            | 0.249           |
+-------------------+-----------------+
| plyfile           | 4.039           |
+-------------------+-----------------+

**Benchmark source:**

.. code:: python

   import time
   from timeit import timeit

   import numpy as np
   import pyvista as pv
   import pyvista_miniply
   import plyfile
   import meshio
   import open3d

   number = 10

   filename = "tmp.ply"
   mesh = pv.Plane(i_resolution=999, j_resolution=999).triangulate()
   mesh.clear_data()
   mesh.save(filename)

   telap = timeit(lambda: pyvista_miniply.read(filename), number=number)
   print(f"pyvista_miniply: {telap/number:.3f}")

   telap = timeit(lambda: open3d.io.read_point_cloud(filename), number=number)
   print(f"open3d:          {telap/number:.3f}")

   telap = timeit(lambda: pv.read(filename), number=number)
   print(f"VTK/PyVista:     {telap/number:.3f}")

   telap = timeit(lambda: meshio.read(filename), number=number)
   print(f"meshio:          {telap/number:.3f}")

   # plyfile
   number = 3  # less because it takes a while
   telap = timeit(lambda: plyfile.PlyData.read(filename), number=number)
   print(f"plyfile:         {telap/number:.3f}")

Comparison with VTK and PyVista
===============================

Here's an additional benchmark comparing VTK/PyVista with
``pyvista-miniply``:

.. code:: python

   import numpy as np
   import time
   import pyvista as pv
   import matplotlib.pyplot as plt
   import pyvista_miniply

   times = []
   filename = 'tmp.ply'
   for res in range(50, 800, 50):
       mesh = pv.Plane(i_resolution=res, j_resolution=res).triangulate().subdivide(2)
       mesh.clear_data()
       mesh.save(filename)

       tstart = time.time()
       pv_mesh = pv.read(filename)
       vtk_time = time.time() - tstart

       tstart = time.time()
       ply_mesh = pyvista_miniply.read_as_mesh(filename)
       ply_reader_time =  time.time() - tstart

       assert np.allclose(pv_mesh['Normals'], ply_mesh['Normals'])
       assert np.allclose(pv_mesh.points, ply_mesh.points)
       assert np.allclose(pv_mesh._connectivity_array, ply_mesh._connectivity_array)

       times.append([mesh.n_points, vtk_time, ply_reader_time])
       print(times[-1])


   times = np.array(times)
   plt.figure(1)
   plt.title('PLY load time')
   plt.plot(times[:, 0], times[:, 1], label='VTK')
   plt.plot(times[:, 0], times[:, 2], label='pyvista-miniply')
   plt.xlabel('Number of Points')
   plt.ylabel('Time to Load (seconds)')
   plt.legend()

   plt.figure(2)
   plt.title('PLY load time (Log-Log)')
   plt.loglog(times[:, 0], times[:, 1], label='VTK')
   plt.loglog(times[:, 0], times[:, 2], label='pyvista-miniply')
   plt.xlabel('Number of Points')
   plt.ylabel('Time to Load (seconds)')
   plt.legend()
   plt.show()

.. image:: https://github.com/pyvista/pyvista-miniply/raw/main/bench0.png

.. image:: https://github.com/pyvista/pyvista-miniply/raw/main/bench1.png

*****************************
 License and Acknowledgments
*****************************

This project relies on ``miniply`` and credit goes to the original
author for the excellent C++ library. That work is licensed under the
MIT License.

The work in this repository is also licensed under the MIT License.

*********
 Support
*********

If you are having issues, please feel free to raise an `Issue
<https://github.com/pyvista/pyvista-miniply/issues>`_.
