import struct import numpy as np from PIL import Image, ImageDraw import os def parse_stl(file_path: str): """Parse an STL file (binary or ASCII) and return mesh data + metadata.""" with open(file_path, 'rb') as f: header = f.read(80) is_binary = False if not header.startswith(b'solid'): is_binary = True else: # Some binary files also start with 'solid', check further with open(file_path, 'rb') as f: f.read(80) tri_count_bytes = f.read(4) if len(tri_count_bytes) == 4: tri_count = struct.unpack('= 4: v = [float(parts[1]), float(parts[2]), float(parts[3])] vertices.append(v) vertices = np.array(vertices, dtype=np.float32) tri_count = len(vertices) // 3 return _compute_metadata(vertices, tri_count) def _compute_metadata(vertices: np.ndarray, tri_count: int): if len(vertices) == 0: return { 'vertices': vertices, 'faces': 0, 'width': 0.0, 'height': 0.0, 'depth': 0.0, } min_v = vertices.min(axis=0) max_v = vertices.max(axis=0) dims = max_v - min_v return { 'vertices': vertices, 'faces': tri_count, 'width': float(dims[0]), 'height': float(dims[1]), 'depth': float(dims[2]), } def generate_thumbnail(vertices: np.ndarray, output_path: str, size: int = 256): """Generate a simple orthographic thumbnail from vertices.""" if len(vertices) == 0: img = Image.new('RGB', (size, size), color=(30, 30, 30)) img.save(output_path) return # Project to XY plane, normalize to image coords min_v = vertices.min(axis=0) max_v = vertices.max(axis=0) dims = max_v - min_v scale = max(dims[0], dims[1]) if scale == 0: scale = 1.0 margin = 20 img_size = size - 2 * margin img = Image.new('RGB', (size, size), color=(30, 30, 30)) draw = ImageDraw.Draw(img) # Draw triangles for i in range(0, len(vertices), 3): tri = vertices[i:i+3] pts = [] for v in tri: x = margin + int(((v[0] - min_v[0]) / scale) * img_size) y = margin + int(((1.0 - (v[1] - min_v[1]) / scale)) * img_size) pts.append((x, y)) if len(pts) == 3: # Simple shading based on Z z_avg = sum(v[2] for v in tri) / 3.0 z_norm = (z_avg - min_v[2]) / (dims[2] if dims[2] > 0 else 1) brightness = int(80 + z_norm * 120) color = (brightness, brightness, int(brightness * 1.1)) draw.polygon(pts, fill=color, outline=(50, 50, 60)) img.save(output_path, 'PNG')