Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-09 10:47:17

0001 #!/usr/bin/env python3
0002 
0003 """
0004 scale_stl.py
0005 
0006 Scale an STL mesh about its centre and write the result to a new STL.
0007 
0008 Usage:
0009     python scale_stl.py input.stl output.stl --scale 1.01
0010 
0011 Examples:
0012     --scale 1.01    Increase dimensions by 1%
0013     --scale 0.99    Decrease dimensions by 1%
0014     --scale 2.0     Double the dimensions
0015 
0016 The centre of the mesh remains unchanged.
0017 """
0018 
0019 import argparse
0020 import sys
0021 from pathlib import Path
0022 
0023 import numpy as np
0024 import trimesh
0025 
0026 
0027 def load_mesh(path):
0028     mesh = trimesh.load_mesh(
0029         path,
0030         force="mesh",
0031         process=True
0032     )
0033 
0034     if not isinstance(mesh, trimesh.Trimesh):
0035         raise ValueError(
0036             "{} did not load as a single mesh.".format(path)
0037         )
0038 
0039     return mesh
0040 
0041 
0042 def scale_mesh_about_centre(mesh, scale):
0043     """
0044     Scale mesh about its centroid.
0045 
0046     x_new = centre + scale * (x - centre)
0047     """
0048 
0049     if scale <= 0:
0050         raise ValueError("Scale factor must be greater than zero.")
0051 
0052     scaled = mesh.copy()
0053 
0054     centre = np.asarray(mesh.centroid, dtype=float)
0055 
0056     scaled.vertices = (
0057         centre
0058         + scale * (mesh.vertices - centre)
0059     )
0060 
0061     return scaled, centre
0062 
0063 
0064 def main():
0065 
0066     parser = argparse.ArgumentParser(
0067         description=(
0068             "Scale an STL mesh about its centre while "
0069             "keeping the centre coordinate unchanged."
0070         )
0071     )
0072 
0073     parser.add_argument(
0074         "input",
0075         help="Input STL file"
0076     )
0077 
0078     parser.add_argument(
0079         "output",
0080         help="Output STL file"
0081     )
0082 
0083     parser.add_argument(
0084         "--scale",
0085         type=float,
0086         required=True,
0087         help=(
0088             "Scale factor. For example, 1.01 increases the "
0089             "dimensions by 1 percent."
0090         )
0091     )
0092 
0093     args = parser.parse_args()
0094 
0095     # Load input mesh
0096     try:
0097         mesh = load_mesh(args.input)
0098     except Exception as exc:
0099         print(
0100             "Error loading mesh: {}".format(exc),
0101             file=sys.stderr
0102         )
0103         sys.exit(1)
0104 
0105     print("Input mesh:")
0106     print("  vertices:   {}".format(len(mesh.vertices)))
0107     print("  faces:      {}".format(len(mesh.faces)))
0108     print("  watertight: {}".format(mesh.is_watertight))
0109     print("  volume:     {}".format(abs(mesh.volume)))
0110 
0111     # Scale
0112     try:
0113         scaled, centre = scale_mesh_about_centre(
0114             mesh,
0115             args.scale
0116         )
0117     except Exception as exc:
0118         print(
0119             "Error scaling mesh: {}".format(exc),
0120             file=sys.stderr
0121         )
0122         sys.exit(1)
0123 
0124     print()
0125     print("Scaling:")
0126     print("  scale factor: {}".format(args.scale))
0127     print(
0128         "  centre: ({:.6g}, {:.6g}, {:.6g})".format(
0129             centre[0],
0130             centre[1],
0131             centre[2]
0132         )
0133     )
0134 
0135     print()
0136     print("Output mesh:")
0137     print("  vertices:   {}".format(len(scaled.vertices)))
0138     print("  faces:      {}".format(len(scaled.faces)))
0139     print("  watertight: {}".format(scaled.is_watertight))
0140     print("  volume:     {}".format(abs(scaled.volume)))
0141 
0142     # Write output
0143     output_path = Path(args.output)
0144 
0145     try:
0146         scaled.export(str(output_path))
0147     except Exception as exc:
0148         print(
0149             "Error writing output: {}".format(exc),
0150             file=sys.stderr
0151         )
0152         sys.exit(1)
0153 
0154     print()
0155     print("Wrote:")
0156     print("  {}".format(output_path))
0157 
0158 
0159 if __name__ == "__main__":
0160     main()