Files
AIO_3D_Print_Web_Platform/stl_merger.py

118 lines
4.6 KiB
Python

import struct
import math
import os
def transform_stl(input_path, output_path, matrix):
# matrix is a flat 16 float array from Three.js (column-major format)
# [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15]
with open(input_path, 'rb') as f:
header = f.read(80)
num_faces = struct.unpack('<I', f.read(4))[0]
faces = f.read()
# Precompute for speed
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
def apply_rot(_nx, _ny, _nz):
# Normals don't need translation, only upper 3x3
# Assuming isotropic scaling for normals, otherwise needs inverse transpose
x = _nx * matrix[0] + _ny * matrix[4] + _nz * matrix[8]
y = _nx * matrix[1] + _ny * matrix[5] + _nz * matrix[9]
z = _nx * matrix[2] + _ny * matrix[6] + _nz * matrix[10]
length = math.sqrt(x*x + y*y + z*z)
if length > 0.000001:
return x/length, y/length, z/length
return 0.0, 0.0, 0.0
out = bytearray(80 + 4 + num_faces * 50)
out[0:80] = header
out[80:84] = struct.pack('<I', num_faces)
offset = 84
src_offset = 0
for _ in range(num_faces):
face_data = faces[src_offset:src_offset+50]
if len(face_data) < 50:
break
nx, ny, nz, v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z, attr = struct.unpack('<12fH', face_data)
# Transform normal
nnx, nny, nnz = apply_rot(nx, ny, nz)
# Transform vertices
nv1x, nv1y, nv1z = apply_m(v1x, v1y, v1z)
nv2x, nv2y, nv2z = apply_m(v2x, v2y, v2z)
nv3x, nv3y, nv3z = apply_m(v3x, v3y, v3z)
struct.pack_into('<12fH', out, offset, nnx, nny, nnz, nv1x, nv1y, nv1z, nv2x, nv2y, nv2z, nv3x, nv3y, nv3z, attr)
offset += 50
src_offset += 50
with open(output_path, 'wb') as f:
f.write(out)
return num_faces
def merge_stls(input_files, output_path):
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)
# transform data
# To speed things up, we could just do the transform in python like above
# For this simple prototype, let's process each file into memory:
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
def apply_rot(_nx, _ny, _nz):
x = _nx * matrix[0] + _ny * matrix[4] + _nz * matrix[8]
y = _nx * matrix[1] + _ny * matrix[5] + _nz * matrix[9]
z = _nx * matrix[2] + _ny * matrix[6] + _nz * matrix[10]
length = math.sqrt(x*x + y*y + z*z)
if length > 0.000001:
return x/length, y/length, z/length
return 0.0, 0.0, 0.0
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)
nnx, nny, nnz = apply_rot(n_x, n_y, n_z)
nv1x, nv1y, nv1z = apply_m(v1x, v1y, v1z)
nv2x, nv2y, nv2z = apply_m(v2x, v2y, v2z)
nv3x, nv3y, nv3z = apply_m(v3x, v3y, v3z)
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
# write merged
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)