Files
AIO_3D_Print_Web_Platform/stl_merger.py

108 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import struct
import math
import os
def merge_stls(input_files, output_path):
try:
from stl import mesh
import numpy as np
meshes = []
for path, matrix in input_files:
try:
# 重新换回轻量级的 numpy-stl 以防内存溢出 (OOM)
m = mesh.Mesh.from_file(path)
mat = np.array(matrix, dtype=np.float64).reshape((4, 4)).T
vectors = m.vectors.reshape(-1, 3)
hom_vectors = np.hstack((vectors, np.ones((len(vectors), 1), dtype=np.float32)))
transformed = (mat @ hom_vectors.T).T
m.vectors = transformed[:, :3].reshape(-1, 3, 3)
# 检测缩放矩阵是否引发镜像翻转 (行列式为负数)
det = np.linalg.det(mat[:3, :3])
if det < 0:
# 发生镜像反转不仅法线会反向三角形三个顶点的顺逆时针Winding Order也会错乱
# 强行交换每个三角形的顶点2和顶点3以纠正渲染正反面
m.vectors[:, [1, 2]] = m.vectors[:, [2, 1]]
m.update_normals()
meshes.append(m)
except Exception as e:
print(f"Error processing path {path} with stl mesh: {e}")
if not meshes:
return
if len(meshes) == 1:
meshes[0].save(output_path)
return
merged_data = np.concatenate([m.data for m in meshes])
merged_mesh = mesh.Mesh(merged_data)
merged_mesh.save(output_path)
return
except Exception as e:
print(f"Mesh fast-merge failed: {e}. Falling back to struct parsing.")
# Extreme fallback just in case no stl libraries work
total_faces = 0
meshes_data = []
for path, matrix in input_files:
with open(path, 'rb') as f:
f.read(80)
faces = struct.unpack('<I', f.read(4))[0]
data = f.read(faces * 50)
def apply_m(_x, _y, _z):
w = _x * matrix[3] + _y * matrix[7] + _z * matrix[11] + matrix[15]
nx = (_x * matrix[0] + _y * matrix[4] + _z * matrix[8] + matrix[12]) / w
ny = (_x * matrix[1] + _y * matrix[5] + _z * matrix[9] + matrix[13]) / w
nz = (_x * matrix[2] + _y * matrix[6] + _z * matrix[10] + matrix[14]) / w
return nx, ny, nz
new_data = bytearray(faces * 50)
src_offset = 0
dst_offset = 0
for _ in range(faces):
n_x, n_y, n_z, v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z, attr = struct.unpack_from('<12fH', data, src_offset)
nv1x, nv1y, nv1z = apply_m(v1x, v1y, v1z)
nv2x, nv2y, nv2z = apply_m(v2x, v2y, v2z)
nv3x, nv3y, nv3z = apply_m(v3x, v3y, v3z)
# Recalculate normal properly using cross product to fix flipped/sheared surfaces
Ux = nv2x - nv1x
Uy = nv2y - nv1y
Uz = nv2z - nv1z
Vx = nv3x - nv1x
Vy = nv3y - nv1y
Vz = nv3z - nv1z
nnx = Uy * Vz - Uz * Vy
nny = Uz * Vx - Ux * Vz
nnz = Ux * Vy - Uy * Vx
l = math.sqrt(nnx**2 + nny**2 + nnz**2)
if l > 1e-8:
nnx, nny, nnz = nnx/l, nny/l, nnz/l
else:
nnx, nny, nnz = 0.0, 0.0, 0.0
struct.pack_into('<12fH', new_data, dst_offset, nnx, nny, nnz, nv1x, nv1y, nv1z, nv2x, nv2y, nv2z, nv3x, nv3y, nv3z, attr)
src_offset += 50
dst_offset += 50
meshes_data.append(new_data)
total_faces += faces
with open(output_path, 'wb') as f:
f.write(b'\0' * 80)
f.write(struct.pack('<I', total_faces))
for d in meshes_data:
f.write(d)