├── .gitignore ├── LICENSE ├── README.md ├── requirements.txt ├── setup.py └── volume_calculator.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | 3 | # Packages 4 | *.egg 5 | *.egg-info 6 | dist 7 | build 8 | eggs 9 | parts 10 | bin 11 | var 12 | sdist 13 | develop-eggs 14 | .installed.cfg 15 | 16 | # Installer logs 17 | pip-log.txt 18 | 19 | # Unit test / coverage reports 20 | .coverage 21 | .tox 22 | 23 | #Translations 24 | *.mo 25 | 26 | #Mr Developer 27 | .mr.developer.cfg 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2012-2020 STL-Volume-Model-Calculator (Mar Canet Sola) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # STL Volume Model Calculator 2 | 3 | This script provides functionality to calculate the volume and surface area of 3D models stored in the STL file format, as well as estimate the weight of the model based on the selected material. It is implemented in Python and supports both binary and ASCII STL files. 4 | 5 | ## Installation 6 | 7 | Make sure you have [Python 3](https://www.python.org/) installed. Then clone the repository and install the required Python libraries using: 8 | 9 | ```bash 10 | pip install -r requirements.txt 11 | ``` 12 | ## Usage 13 | 14 | To use the script, navigate to the directory containing `volume_calculator.py` and your STL file in a terminal, then execute one of the following commands based on your needs: 15 | 16 | ### Volume and Mass Calculation 17 | 18 | ```bash 19 | python volume_calculator.py volume --material [--unit cm|inch] 20 | ``` 21 | ### Surface Area Calculation 22 | ```bash 23 | python volume_calculator.py area 24 | ``` 25 | 26 | ### Arguments: 27 | 28 | : Replace with the path to your STL file. 29 | : Replace with the ID or name of the material you want to use for mass estimation (see the list of materials above). 30 | Options: 31 | 32 | --unit: (Optional) Specify the unit for volume calculation. Choices are cm (default) or inch. 33 | Examples: 34 | 35 | Calculate the volume and mass of torus.stl using ABS material: 36 | 37 | ```bash 38 | python volume_calculator.py torus.stl volume --material ABS 39 | ``` 40 | Calculate the surface area of torus.stl: 41 | ```bash 42 | python volume_calculator.py torus.stl area 43 | ``` 44 | ## Materials Supported 45 | 46 | The script comes with an extensive list of 3D printable materials each with its specified density which is used to calculate the mass of the model. The materials included are: 47 | - ABS 48 | - PLA 49 | - 3k CFRP 50 | - Plexiglass 51 | - Alumide 52 | - Aluminum 53 | - Brass 54 | - Bronze 55 | - Copper 56 | - Gold_14K 57 | - Gold_18K 58 | - Polyamide_MJF 59 | - Polyamide_SLS 60 | - Rubber 61 | - Silver 62 | - Steel 63 | - Titanium 64 | - Resin 65 | - Carbon Steel 66 | - Red Oak 67 | 68 | ## Reporting Issues 69 | Please report any error you may find to me (mar.canet@gmail.com). 70 | 71 | ## Author 72 | Mar Canet Sola(http://var-mar.info) - Twitter: mcanet 73 | 74 | If you want to make a donation you can do in our PayPal account: varvarag@gmail.com 75 | 76 | ## Additional Resources 77 | 78 | If someone is looking for some explanation about volume calculator i recommend read this blog post: http://n-e-r-v-o-u-s.com/blog/?p=4415 79 | 80 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy>=1.19 2 | numpy-stl>=2.0 3 | nibabel>=3.0 4 | pydicom>=2.0 5 | scikit-image>=0.19 6 | tqdm>=4.0 7 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='stl-volume-calculator', 5 | version='1.0.0', 6 | author='Mar Canet', 7 | author_email='mar.canet@gmail.com', 8 | description='Calculate volume and mass of STL models (binary and ASCII), NIfTI, and DICOM files.', 9 | long_description=open('README.md').read(), 10 | long_description_content_type='text/markdown', 11 | url='https://github.com/mcanet/STL-Volume-Model-Calculator', 12 | packages=find_packages(), 13 | install_requires=[ 14 | 'numpy>=1.19', 15 | 'numpy-stl>=2.0', 16 | 'nibabel>=3.0', 17 | 'pydicom>=2.0', 18 | 'scikit-image>=0.19', 19 | 'tqdm>=4.0' 20 | ], 21 | entry_points={ 22 | 'console_scripts': [ 23 | 'volume_calculator=volume_calculator:main', 24 | ], 25 | }, 26 | classifiers=[ 27 | 'Programming Language :: Python :: 3', 28 | 'License :: OSI Approved :: MIT License', 29 | 'Operating System :: OS Independent', 30 | ], 31 | python_requires='>=3.6', 32 | ) 33 | -------------------------------------------------------------------------------- /volume_calculator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | ''' 4 | VOLUME CALCULATION STL MODELS 5 | Author: Mar Canet (mar.canet@gmail.com) - September 2012-2023 6 | Description: Calculate volume and mass of STL models (binary and ASCII), NIfTI, and DICOM files. 7 | ''' 8 | 9 | import struct 10 | import sys 11 | import re 12 | import argparse 13 | from tqdm import tqdm # Add the tqdm library 14 | 15 | class materialsFor3DPrinting: 16 | def __init__(self): 17 | self.materials_dict = { 18 | 1: {'name': 'ABS', 'mass': 1.02}, 19 | 2: {'name': 'PLA', 'mass': 1.25}, 20 | 3: {'name': '3k CFRP', 'mass': 1.79}, 21 | 4: {'name': 'Plexiglass', 'mass': 1.18}, 22 | 5: {'name': 'Alumide', 'mass': 1.36}, 23 | 6: {'name': 'Aluminum', 'mass': 2.698}, 24 | 7: {'name': 'Brass', 'mass': 8.6}, 25 | 8: {'name': 'Bronze', 'mass': 9.0}, 26 | 9: {'name': 'Copper', 'mass': 9.0}, 27 | 10: {'name': 'Gold_14K', 'mass': 13.6}, 28 | 11: {'name': 'Gold_18K', 'mass': 15.6}, 29 | 12: {'name': 'Polyamide_MJF', 'mass': 1.01}, 30 | 13: {'name': 'Polyamide_SLS', 'mass': 0.95}, 31 | 14: {'name': 'Rubber', 'mass': 1.2}, 32 | 15: {'name': 'Silver', 'mass': 10.26}, 33 | 16: {'name': 'Steel', 'mass': 7.86}, 34 | 17: {'name': 'Titanium', 'mass': 4.41}, 35 | 18: {'name': 'Resin', 'mass': 1.2}, 36 | 19: {'name': 'Carbon Steel', 'mass': 7.800}, 37 | 20: {'name': 'Red Oak', 'mass': 5.700} 38 | } 39 | 40 | def get_material_mass(self, material_identifier): 41 | if material_identifier is None: 42 | return 1 # Default mass (density) value if no material is specified 43 | elif isinstance(material_identifier, int) and material_identifier in self.materials_dict: 44 | return self.materials_dict[material_identifier]['mass'] 45 | elif isinstance(material_identifier, str): 46 | for key, value in self.materials_dict.items(): 47 | if value['name'].lower() == material_identifier.lower(): 48 | return value['mass'] 49 | raise ValueError(f"Invalid material name: {material_identifier}") 50 | else: 51 | raise ValueError(f"Invalid material identifier: {material_identifier}") 52 | 53 | def list_materials(self): 54 | for key, value in self.materials_dict.items(): 55 | print(f"{key} = {value['name']}") 56 | 57 | class STLUtils: 58 | def __init__(self): 59 | self.f = None 60 | self.is_binary_file = None 61 | self.triangles = [] 62 | 63 | def is_binary(self, file): 64 | with open(file, 'rb') as f: 65 | header = f.read(80).decode(errors='replace') 66 | return not header.startswith('solid') 67 | 68 | def read_ascii_triangle(self, lines, index): 69 | p1 = list(map(float, re.findall(r"[-+]?\d*\.\d+|\d+", lines[index + 1]))) 70 | p2 = list(map(float, re.findall(r"[-+]?\d*\.\d+|\d+", lines[index + 2]))) 71 | p3 = list(map(float, re.findall(r"[-+]?\d*\.\d+|\d+", lines[index + 3]))) 72 | return self.signedVolumeOfTriangle(p1, p2, p3) 73 | 74 | def signedVolumeOfTriangle(self, p1, p2, p3): 75 | v321 = p3[0] * p2[1] * p1[2] 76 | v231 = p2[0] * p3[1] * p1[2] 77 | v312 = p3[0] * p1[1] * p2[2] 78 | v132 = p1[0] * p3[1] * p2[2] 79 | v213 = p2[0] * p1[1] * p3[2] 80 | v123 = p1[0] * p2[1] * p3[2] 81 | return (1.0 / 6.0) * (-v321 + v231 + v312 - v132 - v213 + v123) 82 | 83 | def unpack(self, sig, l): 84 | s = self.f.read(l) 85 | return struct.unpack(sig, s) 86 | 87 | def read_triangle(self): 88 | n = self.unpack("<3f", 12) 89 | p1 = self.unpack("<3f", 12) 90 | p2 = self.unpack("<3f", 12) 91 | p3 = self.unpack("<3f", 12) 92 | self.unpack("