1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
  
     | 
    
      #!/usr/bin/python3
#
# Simple tool to validate an STL.
# It checks for:
# o Any occurrence of "nan" or "inf" vertices or normals
# o Any non-manifold (dangling) edges.
#
# Usage: validatestl.py <file.stl>
#
# Based on code by Jan Squirrel Koniarik from:
# https://github.com/SquirrelCZE/pycad/
#
# Author: Marius Kintel <marius@kintel.net>
# Licence: GPL V2
#
import sys
import io
import hashlib
import os
import subprocess
import struct
import math
from collections import Counter
def read_stl(filename):
    triangles = list()
    with open(filename, "rb") as fd:
        start = fd.read(5)
        if start == b'solid':
            stl_type = 'ascii'
        else:
            stl_type = 'binary'
  
    print(start)
    if stl_type == 'ascii':
        with open(filename, "r") as fd:
            triangle = {
                'normal': [0, 0, 0],
                'points': list()
            }
            for line in fd:
                line = line.strip()
                if line.startswith('solid'):
                    continue
                elif line.startswith('endsolid'):
                    continue
                elif line.startswith('outer'):
                    continue
                elif line.startswith('facet'):
                    parts = line.split(' ')
                    for i in range(2, 5):
                        triangle['normal'][i-2] = float(parts[i])
                    continue
                elif line.startswith('vertex'):
                    parts = line.split(' ')
                    point = [0, 0, 0]
                    for i in range(1, 4):
                        point[i-1] = float(parts[i])
                    triangle['points'].append(point)
                    continue
                elif line.startswith('endloop'):
                    continue
                elif line.startswith('endfacet'):
                    triangles.append(triangle)
                    triangle = {
                        "normal": [0, 0, 0],
                        "points": list()
                    }
                    continue
    else:  # binary
        with open(filename, "rb") as fd:
            data = fd.read()
            count = int.from_bytes(data[80:84], byteorder='little')
            for offset in range(84, 84+count*(12*4+2), 12*4+2):
                try:
                    triangle = {
                        'points': list()
                    }
                    triangle['normal'] = list(
                        struct.unpack('fff', data[offset:offset+12]))
                    for p in range(1,4):
                        pnt = struct.unpack('fff',
                            data[offset+p*12:offset+p*12+12])
                        triangle['points'].append(pnt)
                    triangles.append(triangle)
                except struct.error:
                    print("Invalid binary stl format")
                    return None
    return Mesh(
        triangles=triangles
    )
class Mesh():
    def __init__(self, triangles):
        points = list()
        p_triangles = list()
        p_normals = list()
        for triangle in triangles:
            p_normals.append(triangle['normal'])
            p_triangle = list()
            for point in triangle['points']:
                if point not in points:
                    points.append(point)
                p_triangle.append(
                    points.index(point)
                )
            p_triangles.append(p_triangle)
        self.points = points
        self.triangles = p_triangles
        self.normals = p_normals
def validateSTL(filename):
    mesh = read_stl(filename);
    if mesh is None:
        print("Loading error")
        return False
    if len(mesh.triangles) < 1:
        print("No triangles found")
        return False
    
    if len([n[i] for i in range(0,3) for n in mesh.points if math.isinf(n[i]) or math.isnan(n[i])]):
        print("NaN of Inf vertices found")
        return False
    if len([n[i] for i in range(0,3) for n in mesh.normals if math.isinf(n[i]) or math.isnan(n[i])]):
        print("NaN of Inf normals found")
        return False
    edges = Counter((t[i], t[(i+1)%3]) for i in range(0,3) for t in mesh.triangles)
    reverse_edges = Counter((t[(i+1)%3], t[i]) for i in range(0,3) for t in mesh.triangles)
    edges.subtract(reverse_edges)
    edges += Counter() # remove zero and negative counts
    if len(edges) > 0:
        print("Non-manifold STL: " + str(edges))
        return False
    return True
if __name__ == "__main__":
    retval = validateSTL(sys.argv[1])
    if retval:
        sys.exit(0)
    else:
        sys.exit(1)
 
     |