{"id":13658128,"url":"https://github.com/WoLpH/numpy-stl","last_synced_at":"2025-04-24T08:31:21.499Z","repository":{"id":21985628,"uuid":"25310569","full_name":"wolph/numpy-stl","owner":"wolph","description":"Simple library to make working with STL files (and 3D objects in general) fast and easy.","archived":false,"fork":false,"pushed_at":"2025-02-03T12:14:26.000Z","size":1364,"stargazers_count":649,"open_issues_count":1,"forks_count":109,"subscribers_count":32,"default_branch":"develop","last_synced_at":"2025-04-19T10:32:12.857Z","etag":null,"topics":["3d","fast","high-performance","numpy","python","python2","python3","stl"],"latest_commit_sha":null,"homepage":"http://numpy-stl.readthedocs.org/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wolph.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":"CONTRIBUTING.rst","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"wolph"}},"created_at":"2014-10-16T16:15:59.000Z","updated_at":"2025-04-16T19:39:28.000Z","dependencies_parsed_at":"2023-01-13T21:47:04.806Z","dependency_job_id":"dbf692c4-f460-432b-9ce6-13607bfb4f6a","html_url":"https://github.com/wolph/numpy-stl","commit_stats":{"total_commits":425,"total_committers":23,"mean_commits":18.47826086956522,"dds":0.2988235294117647,"last_synced_commit":"3cf488b184a62dfbc44f5d66ffc8f4ab912a18ee"},"previous_names":[],"tags_count":55,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wolph%2Fnumpy-stl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wolph%2Fnumpy-stl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wolph%2Fnumpy-stl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wolph%2Fnumpy-stl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wolph","download_url":"https://codeload.github.com/wolph/numpy-stl/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250591985,"owners_count":21455476,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["3d","fast","high-performance","numpy","python","python2","python3","stl"],"created_at":"2024-08-02T05:00:56.586Z","updated_at":"2025-04-24T08:31:21.485Z","avatar_url":"https://github.com/wolph.png","language":"Python","readme":"numpy-stl\n==============================================================================\n\n.. image:: https://github.com/WoLpH/numpy-stl/actions/workflows/main.yml/badge.svg?branch=master\n    :alt: numpy-stl test status \n    :target: https://github.com/WoLpH/numpy-stl/actions/workflows/main.yml\n\n.. image:: https://ci.appveyor.com/api/projects/status/cbv7ak2i59wf3lpj?svg=true\n    :alt: numpy-stl test status \n    :target: https://ci.appveyor.com/project/WoLpH/numpy-stl\n\n.. image:: https://badge.fury.io/py/numpy-stl.svg\n    :alt: numpy-stl Pypi version \n    :target: https://pypi.python.org/pypi/numpy-stl\n\n.. image:: https://coveralls.io/repos/WoLpH/numpy-stl/badge.svg?branch=master\n    :alt: numpy-stl code coverage \n    :target: https://coveralls.io/r/WoLpH/numpy-stl?branch=master\n\n.. image:: https://img.shields.io/pypi/pyversions/numpy-stl.svg\n\nSimple library to make working with STL files (and 3D objects in general) fast\nand easy.\n\nDue to all operations heavily relying on `numpy` this is one of the fastest\nSTL editing libraries for Python available.\n\nSecurity contact information\n------------------------------------------------------------------------------\n\nTo report a security vulnerability, please use the\n`Tidelift security contact \u003chttps://tidelift.com/security\u003e`_.\nTidelift will coordinate the fix and disclosure.\n\nIssues\n------\n\nIf you encounter any issues, make sure you report them `here \u003chttps://github.com/WoLpH/numpy-stl/issues\u003e`_. Be sure to search for existing issues however. Many issues have been covered before.\nWhile this project uses `numpy` as it's main dependency, it is not in any way affiliated to the `numpy` project or the NumFocus organisation.\n\nLinks\n-----\n\n - The source: https://github.com/WoLpH/numpy-stl\n - Project page: https://pypi.python.org/pypi/numpy-stl\n - Reporting bugs: https://github.com/WoLpH/numpy-stl/issues\n - Documentation: http://numpy-stl.readthedocs.org/en/latest/\n - My blog: https://wol.ph/\n\nRequirements for installing:\n------------------------------------------------------------------------------\n\n - `numpy`_ any recent version\n - `python-utils`_ version 1.6 or greater\n\nInstallation:\n------------------------------------------------------------------------------\n\n`pip install numpy-stl`\n\nInitial usage:\n------------------------------------------------------------------------------\n\nAfter installing the package, you should be able to run the following commands\nsimilar to how you can run `pip`.\n\n.. code-block:: shell\n \n   $ stl2bin your_ascii_stl_file.stl new_binary_stl_file.stl\n   $ stl2ascii your_binary_stl_file.stl new_ascii_stl_file.stl\n   $ stl your_ascii_stl_file.stl new_binary_stl_file.stl\n\nContributing:\n------------------------------------------------------------------------------\n\nContributions are always welcome. Please view the guidelines to get started:\nhttps://github.com/WoLpH/numpy-stl/blob/develop/CONTRIBUTING.rst\n\nQuickstart\n------------------------------------------------------------------------------\n\n.. code-block:: python\n\n    import numpy\n    from stl import mesh\n\n    # Using an existing stl file:\n    your_mesh = mesh.Mesh.from_file('some_file.stl')\n\n    # Or creating a new mesh (make sure not to overwrite the `mesh` import by\n    # naming it `mesh`):\n    VERTICE_COUNT = 100\n    data = numpy.zeros(VERTICE_COUNT, dtype=mesh.Mesh.dtype)\n    your_mesh = mesh.Mesh(data, remove_empty_areas=False)\n\n    # The mesh normals (calculated automatically)\n    your_mesh.normals\n    # The mesh vectors\n    your_mesh.v0, your_mesh.v1, your_mesh.v2\n    # Accessing individual points (concatenation of v0, v1 and v2 in triplets)\n    assert (your_mesh.points[0][0:3] == your_mesh.v0[0]).all()\n    assert (your_mesh.points[0][3:6] == your_mesh.v1[0]).all()\n    assert (your_mesh.points[0][6:9] == your_mesh.v2[0]).all()\n    assert (your_mesh.points[1][0:3] == your_mesh.v0[1]).all()\n\n    your_mesh.save('new_stl_file.stl')\n\nPlotting using `matplotlib`_ is equally easy:\n------------------------------------------------------------------------------\n\n.. code-block:: python\n\n    from stl import mesh\n    from mpl_toolkits import mplot3d\n    from matplotlib import pyplot\n\n    # Create a new plot\n    figure = pyplot.figure()\n    axes = figure.add_subplot(projection='3d')\n\n    # Load the STL files and add the vectors to the plot\n    your_mesh = mesh.Mesh.from_file('tests/stl_binary/HalfDonut.stl')\n    axes.add_collection3d(mplot3d.art3d.Poly3DCollection(your_mesh.vectors))\n\n    # Auto scale to the mesh size\n    scale = your_mesh.points.flatten()\n    axes.auto_scale_xyz(scale, scale, scale)\n\n    # Show the plot to the screen\n    pyplot.show()\n\n.. _numpy: http://numpy.org/\n.. _matplotlib: http://matplotlib.org/\n.. _python-utils: https://github.com/WoLpH/python-utils\n\nExperimental support for reading 3MF files\n------------------------------------------------------------------------------\n\n.. code-block:: python\n\n    import pathlib\n    import stl\n\n    path = pathlib.Path('tests/3mf/Moon.3mf')\n\n    # Load the 3MF file\n    for m in stl.Mesh.from_3mf_file(path):\n        # Do something with the mesh\n        print('mesh', m)\n\nNote that this is still experimental and may not work for all 3MF files.\nAdditionally it only allows reading 3mf files, not writing them.\n\nModifying Mesh objects\n------------------------------------------------------------------------------\n\n.. code-block:: python\n\n    from stl import mesh\n    import math\n    import numpy\n\n    # Create 3 faces of a cube\n    data = numpy.zeros(6, dtype=mesh.Mesh.dtype)\n\n    # Top of the cube\n    data['vectors'][0] = numpy.array([[0, 1, 1],\n                                      [1, 0, 1],\n                                      [0, 0, 1]])\n    data['vectors'][1] = numpy.array([[1, 0, 1],\n                                      [0, 1, 1],\n                                      [1, 1, 1]])\n    # Front face\n    data['vectors'][2] = numpy.array([[1, 0, 0],\n                                      [1, 0, 1],\n                                      [1, 1, 0]])\n    data['vectors'][3] = numpy.array([[1, 1, 1],\n                                      [1, 0, 1],\n                                      [1, 1, 0]])\n    # Left face\n    data['vectors'][4] = numpy.array([[0, 0, 0],\n                                      [1, 0, 0],\n                                      [1, 0, 1]])\n    data['vectors'][5] = numpy.array([[0, 0, 0],\n                                      [0, 0, 1],\n                                      [1, 0, 1]])\n\n    # Since the cube faces are from 0 to 1 we can move it to the middle by\n    # substracting .5\n    data['vectors'] -= .5\n\n    # Generate 4 different meshes so we can rotate them later\n    meshes = [mesh.Mesh(data.copy()) for _ in range(4)]\n\n    # Rotate 90 degrees over the Y axis\n    meshes[0].rotate([0.0, 0.5, 0.0], math.radians(90))\n\n    # Translate 2 points over the X axis\n    meshes[1].x += 2\n\n    # Rotate 90 degrees over the X axis\n    meshes[2].rotate([0.5, 0.0, 0.0], math.radians(90))\n    # Translate 2 points over the X and Y points\n    meshes[2].x += 2\n    meshes[2].y += 2\n\n    # Rotate 90 degrees over the X and Y axis\n    meshes[3].rotate([0.5, 0.0, 0.0], math.radians(90))\n    meshes[3].rotate([0.0, 0.5, 0.0], math.radians(90))\n    # Translate 2 points over the Y axis\n    meshes[3].y += 2\n\n\n    # Optionally render the rotated cube faces\n    from matplotlib import pyplot\n    from mpl_toolkits import mplot3d\n\n    # Create a new plot\n    figure = pyplot.figure()\n    axes = figure.add_subplot(projection='3d')\n\n    # Render the cube faces\n    for m in meshes:\n        axes.add_collection3d(mplot3d.art3d.Poly3DCollection(m.vectors))\n\n    # Auto scale to the mesh size\n    scale = numpy.concatenate([m.points for m in meshes]).flatten()\n    axes.auto_scale_xyz(scale, scale, scale)\n\n    # Show the plot to the screen\n    pyplot.show()\n\nExtending Mesh objects\n------------------------------------------------------------------------------\n\n.. code-block:: python\n\n    from stl import mesh\n    import math\n    import numpy\n\n    # Create 3 faces of a cube\n    data = numpy.zeros(6, dtype=mesh.Mesh.dtype)\n\n    # Top of the cube\n    data['vectors'][0] = numpy.array([[0, 1, 1],\n                                      [1, 0, 1],\n                                      [0, 0, 1]])\n    data['vectors'][1] = numpy.array([[1, 0, 1],\n                                      [0, 1, 1],\n                                      [1, 1, 1]])\n    # Front face\n    data['vectors'][2] = numpy.array([[1, 0, 0],\n                                      [1, 0, 1],\n                                      [1, 1, 0]])\n    data['vectors'][3] = numpy.array([[1, 1, 1],\n                                      [1, 0, 1],\n                                      [1, 1, 0]])\n    # Left face\n    data['vectors'][4] = numpy.array([[0, 0, 0],\n                                      [1, 0, 0],\n                                      [1, 0, 1]])\n    data['vectors'][5] = numpy.array([[0, 0, 0],\n                                      [0, 0, 1],\n                                      [1, 0, 1]])\n\n    # Since the cube faces are from 0 to 1 we can move it to the middle by\n    # substracting .5\n    data['vectors'] -= .5\n\n    cube_back = mesh.Mesh(data.copy())\n    cube_front = mesh.Mesh(data.copy())\n\n    # Rotate 90 degrees over the X axis followed by the Y axis followed by the\n    # X axis\n    cube_back.rotate([0.5, 0.0, 0.0], math.radians(90))\n    cube_back.rotate([0.0, 0.5, 0.0], math.radians(90))\n    cube_back.rotate([0.5, 0.0, 0.0], math.radians(90))\n\n    cube = mesh.Mesh(numpy.concatenate([\n        cube_back.data.copy(),\n        cube_front.data.copy(),\n    ]))\n\n    # Optionally render the rotated cube faces\n    from matplotlib import pyplot\n    from mpl_toolkits import mplot3d\n\n    # Create a new plot\n    figure = pyplot.figure()\n    axes = figure.add_subplot(projection='3d')\n\n    # Render the cube\n    axes.add_collection3d(mplot3d.art3d.Poly3DCollection(cube.vectors))\n\n    # Auto scale to the mesh size\n    scale = cube_back.points.flatten()\n    axes.auto_scale_xyz(scale, scale, scale)\n\n    # Show the plot to the screen\n    pyplot.show()\n\nCreating a single triangle\n----------------------------------\n\n.. code-block:: python\n\n    import numpy\n    from stl import mesh\n\n    # A unit triangle\n    tri_vectors = [[0,0,0],[0,1,0],[0,0,1]]\n\n    # Create the vector data. It’s a numpy structured array with N entries, where N is the number of triangles (here N=1), and each entry is in the format ('normals','vectors','attr')\n    data = numpy.array([(\n        0, # Set 'normals' to zero, and the mesh class will automatically calculate them at initialization\n        tri_vectors, # 'vectors'\n        0 # 'attr'\n    )], dtype = mesh.Mesh.dtype) # The structure defined by the mesh class (N x ('normals','vectors','attr'))\n\n    # Create the mesh object from the structured array\n    tri_mesh = mesh.Mesh(data)\n\n    # Optionally make a plot for fun\n    # Load the plot tools\n    from matplotlib import pyplot\n    from mpl_toolkits import mplot3d\n\n    # Create a new plot\n    figure = pyplot.figure()\n    axes = figure.add_subplot(projection='3d')\n\n    # Add mesh to plot\n    axes.add_collection3d(mplot3d.art3d.Poly3DCollection(tri_mesh.vectors)) # Just need the 'vectors' attribute for display\n\nCreating Mesh objects from a list of vertices and faces\n------------------------------------------------------------------------------\n\n.. code-block:: python\n\n    import numpy as np\n    from stl import mesh\n\n    # Define the 8 vertices of the cube\n    vertices = np.array([\\\n        [-1, -1, -1],\n        [+1, -1, -1],\n        [+1, +1, -1],\n        [-1, +1, -1],\n        [-1, -1, +1],\n        [+1, -1, +1],\n        [+1, +1, +1],\n        [-1, +1, +1]])\n    # Define the 12 triangles composing the cube\n    faces = np.array([\\\n        [0,3,1],\n        [1,3,2],\n        [0,4,7],\n        [0,7,3],\n        [4,5,6],\n        [4,6,7],\n        [5,1,2],\n        [5,2,6],\n        [2,3,6],\n        [3,7,6],\n        [0,1,5],\n        [0,5,4]])\n\n    # Create the mesh\n    cube = mesh.Mesh(np.zeros(faces.shape[0], dtype=mesh.Mesh.dtype))\n    for i, f in enumerate(faces):\n        for j in range(3):\n            cube.vectors[i][j] = vertices[f[j],:]\n\n    # Write the mesh to file \"cube.stl\"\n    cube.save('cube.stl')\n\n\nEvaluating Mesh properties (Volume, Center of gravity, Inertia, Convexity)\n------------------------------------------------------------------------------\n\n.. code-block:: python\n\n    import numpy as np\n    from stl import mesh\n\n    # Using an existing closed stl file:\n    your_mesh = mesh.Mesh.from_file('some_file.stl')\n\n    volume, cog, inertia = your_mesh.get_mass_properties()\n    print(\"Volume                                  = {0}\".format(volume))\n    print(\"Position of the center of gravity (COG) = {0}\".format(cog))\n    print(\"Inertia matrix at expressed at the COG  = {0}\".format(inertia[0,:]))\n    print(\"                                          {0}\".format(inertia[1,:]))\n    print(\"                                          {0}\".format(inertia[2,:]))\n    print(\"Your mesh is convex: {0}\".format(your_mesh.is_convex()))\nCombining multiple STL files\n------------------------------------------------------------------------------\n\n.. code-block:: python\n\n    import math\n    import stl\n    from stl import mesh\n    import numpy\n\n\n    # find the max dimensions, so we can know the bounding box, getting the height,\n    # width, length (because these are the step size)...\n    def find_mins_maxs(obj):\n        minx = obj.x.min()\n        maxx = obj.x.max()\n        miny = obj.y.min()\n        maxy = obj.y.max()\n        minz = obj.z.min()\n        maxz = obj.z.max()\n        return minx, maxx, miny, maxy, minz, maxz\n\n\n    def translate(_solid, step, padding, multiplier, axis):\n        if 'x' == axis:\n            items = 0, 3, 6\n        elif 'y' == axis:\n            items = 1, 4, 7\n        elif 'z' == axis:\n            items = 2, 5, 8\n        else:\n            raise RuntimeError('Unknown axis %r, expected x, y or z' % axis)\n\n        # _solid.points.shape == [:, ((x, y, z), (x, y, z), (x, y, z))]\n        _solid.points[:, items] += (step * multiplier) + (padding * multiplier)\n\n\n    def copy_obj(obj, dims, num_rows, num_cols, num_layers):\n        w, l, h = dims\n        copies = []\n        for layer in range(num_layers):\n            for row in range(num_rows):\n                for col in range(num_cols):\n                    # skip the position where original being copied is\n                    if row == 0 and col == 0 and layer == 0:\n                        continue\n                    _copy = mesh.Mesh(obj.data.copy())\n                    # pad the space between objects by 10% of the dimension being\n                    # translated\n                    if col != 0:\n                        translate(_copy, w, w / 10., col, 'x')\n                    if row != 0:\n                        translate(_copy, l, l / 10., row, 'y')\n                    if layer != 0:\n                        translate(_copy, h, h / 10., layer, 'z')\n                    copies.append(_copy)\n        return copies\n\n    # Using an existing stl file:\n    main_body = mesh.Mesh.from_file('ball_and_socket_simplified_-_main_body.stl')\n\n    # rotate along Y\n    main_body.rotate([0.0, 0.5, 0.0], math.radians(90))\n\n    minx, maxx, miny, maxy, minz, maxz = find_mins_maxs(main_body)\n    w1 = maxx - minx\n    l1 = maxy - miny\n    h1 = maxz - minz\n    copies = copy_obj(main_body, (w1, l1, h1), 2, 2, 1)\n\n    # I wanted to add another related STL to the final STL\n    twist_lock = mesh.Mesh.from_file('ball_and_socket_simplified_-_twist_lock.stl')\n    minx, maxx, miny, maxy, minz, maxz = find_mins_maxs(twist_lock)\n    w2 = maxx - minx\n    l2 = maxy - miny\n    h2 = maxz - minz\n    translate(twist_lock, w1, w1 / 10., 3, 'x')\n    copies2 = copy_obj(twist_lock, (w2, l2, h2), 2, 2, 1)\n    combined = mesh.Mesh(numpy.concatenate([main_body.data, twist_lock.data] +\n                                        [copy.data for copy in copies] +\n                                        [copy.data for copy in copies2]))\n\n    combined.save('combined.stl', mode=stl.Mode.ASCII)  # save as ASCII\n\nKnown limitations\n------------------------------------------------------------------------------\n\n - When speedups are enabled the STL name is automatically converted to\n   lowercase.\n","funding_links":["https://github.com/sponsors/wolph","https://tidelift.com/security"],"categories":["Python"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FWoLpH%2Fnumpy-stl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FWoLpH%2Fnumpy-stl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FWoLpH%2Fnumpy-stl/lists"}