wiki/mandates/pek/testing/create_bostich_3ds.py
ValueOn AG 15f0e51bd0 pek
2025-10-24 21:42:47 +02:00

266 lines
9.2 KiB
Python

#!/usr/bin/env python3
"""
Create 3DS file for LEITZ Bostitch 2-hole punch
Based on photos showing 20cm x 10cm x 10cm dimensions
"""
import struct
import math
def _write3dsHeader(f):
"""Write 3DS file header"""
# Main chunk header
f.write(b'\x4D\x4D') # Magic number
f.write(struct.pack('<I', 0)) # File size (will be updated later)
f.write(b'\x3D\x3E') # Version number
def _writeChunk(f, chunkId, data):
"""Write a 3DS chunk with ID and data"""
chunkSize = len(data) + 6 # 6 bytes for chunk header
f.write(struct.pack('<H', chunkId)) # Chunk ID
f.write(struct.pack('<I', chunkSize)) # Chunk size
f.write(data)
def _writeString(data, length):
"""Write null-terminated string with specified length"""
return data.encode('ascii')[:length-1].ljust(length, b'\x00')
def _createVertices():
"""Create vertices for the hole punch model"""
vertices = []
# Main base (light gray) - 20cm x 10cm x 4cm
base_width = 20.0
base_depth = 10.0
base_height = 4.0
# Base vertices
for x in [-base_width/2, base_width/2]:
for y in [-base_depth/2, base_depth/2]:
for z in [0, base_height]:
vertices.append((x, y, z))
# Upper mechanism (light gray) - 16cm x 8cm x 3cm
mech_width = 16.0
mech_depth = 8.0
mech_height = 3.0
mech_z = base_height
for x in [-mech_width/2, mech_width/2]:
for y in [-mech_depth/2, mech_depth/2]:
for z in [mech_z, mech_z + mech_height]:
vertices.append((x, y, z))
# Lever (dark blue) - 12cm x 6cm x 2cm, curved
lever_width = 12.0
lever_depth = 6.0
lever_height = 2.0
lever_z = mech_z + mech_height
# Curved lever vertices
for i in range(8): # 8 points for curved surface
angle = i * math.pi / 7 # 0 to π
x = (lever_width/2) * math.cos(angle)
y = (lever_depth/2) * math.sin(angle)
vertices.append((x, y, lever_z))
vertices.append((x, y, lever_z + lever_height))
# Paper guide (white) - 2cm x 8cm x 0.5cm
guide_width = 2.0
guide_depth = 8.0
guide_height = 0.5
guide_x = base_width/2 + 1.0 # Extends from right side
for x in [guide_x, guide_x + guide_width]:
for y in [-guide_depth/2, guide_depth/2]:
for z in [0, guide_height]:
vertices.append((x, y, z))
# Punch holes (metallic cylinders)
hole_radius = 0.3
hole_height = 8.0
hole_positions = [(-2.0, 0), (2.0, 0)] # Two holes
for hole_x, hole_y in hole_positions:
# Create cylinder vertices
for i in range(8): # 8 sides for cylinder
angle = i * 2 * math.pi / 8
x = hole_x + hole_radius * math.cos(angle)
y = hole_y + hole_radius * math.sin(angle)
vertices.append((x, y, mech_z))
vertices.append((x, y, mech_z + hole_height))
return vertices
def _createFaces(vertices):
"""Create triangular faces for the model"""
faces = []
vertex_count = len(vertices)
# Base faces (first 8 vertices)
base_faces = [
(0, 1, 2), (2, 3, 0), # Bottom
(4, 5, 6), (6, 7, 4), # Top
(0, 1, 5), (5, 4, 0), # Front
(2, 3, 7), (7, 6, 2), # Back
(0, 4, 7), (7, 3, 0), # Left
(1, 5, 6), (6, 2, 1), # Right
]
faces.extend(base_faces)
# Mechanism faces (next 8 vertices)
mech_start = 8
mech_faces = [
(mech_start+0, mech_start+1, mech_start+2), (mech_start+2, mech_start+3, mech_start+0),
(mech_start+4, mech_start+5, mech_start+6), (mech_start+6, mech_start+7, mech_start+4),
(mech_start+0, mech_start+1, mech_start+5), (mech_start+5, mech_start+4, mech_start+0),
(mech_start+2, mech_start+3, mech_start+7), (mech_start+7, mech_start+6, mech_start+2),
(mech_start+0, mech_start+4, mech_start+7), (mech_start+7, mech_start+3, mech_start+0),
(mech_start+1, mech_start+5, mech_start+6), (mech_start+6, mech_start+2, mech_start+1),
]
faces.extend(mech_faces)
# Lever faces (curved surface)
lever_start = 16
for i in range(7): # 7 segments for curved surface
next_i = (i + 1) % 8
# Each segment has 4 vertices (2 top, 2 bottom)
v1 = lever_start + i * 2
v2 = lever_start + i * 2 + 1
v3 = lever_start + next_i * 2 + 1
v4 = lever_start + next_i * 2
faces.append((v1, v2, v3))
faces.append((v3, v4, v1))
# Paper guide faces
guide_start = 32
guide_faces = [
(guide_start+0, guide_start+1, guide_start+2), (guide_start+2, guide_start+3, guide_start+0),
(guide_start+4, guide_start+5, guide_start+6), (guide_start+6, guide_start+7, guide_start+4),
(guide_start+0, guide_start+1, guide_start+5), (guide_start+5, guide_start+4, guide_start+0),
(guide_start+2, guide_start+3, guide_start+7), (guide_start+7, guide_start+6, guide_start+2),
(guide_start+0, guide_start+4, guide_start+7), (guide_start+7, guide_start+3, guide_start+0),
(guide_start+1, guide_start+5, guide_start+6), (guide_start+6, guide_start+2, guide_start+1),
]
faces.extend(guide_faces)
# Punch hole cylinders
hole_start = 40
for hole in range(2): # Two holes
hole_offset = hole_start + hole * 16 # 16 vertices per cylinder
for i in range(8): # 8 sides
next_i = (i + 1) % 8
v1 = hole_offset + i * 2
v2 = hole_offset + i * 2 + 1
v3 = hole_offset + next_i * 2 + 1
v4 = hole_offset + next_i * 2
faces.append((v1, v2, v3))
faces.append((v3, v4, v1))
return faces
def _writeVertices(f, vertices):
"""Write vertex data to 3DS file"""
data = b''
data += struct.pack('<H', len(vertices)) # Number of vertices
for x, y, z in vertices:
data += struct.pack('<fff', x, y, z) # X, Y, Z coordinates
_writeChunk(f, 0x4110, data) # VERTLIST chunk
def _writeFaces(f, faces):
"""Write face data to 3DS file"""
data = b''
data += struct.pack('<H', len(faces)) # Number of faces
for face in faces:
data += struct.pack('<HHH', face[0], face[1], face[2]) # Three vertex indices
data += struct.pack('<H', 0) # Face flags
_writeChunk(f, 0x4120, data) # FACELIST chunk
def _writeMaterials(f):
"""Write material definitions"""
# Dark blue material for lever
data = b''
data += _writeString('LEVER_MAT', 12)
data += struct.pack('<fff', 0.2, 0.2, 0.6) # Diffuse color (dark blue)
data += struct.pack('<fff', 0.1, 0.1, 0.3) # Ambient color
data += struct.pack('<fff', 0.8, 0.8, 0.9) # Specular color
data += struct.pack('<f', 100.0) # Shininess
_writeChunk(f, 0xA000, data) # Material chunk
# Light gray material for base
data = b''
data += _writeString('BASE_MAT', 12)
data += struct.pack('<fff', 0.7, 0.7, 0.7) # Diffuse color (light gray)
data += struct.pack('<fff', 0.3, 0.3, 0.3) # Ambient color
data += struct.pack('<fff', 0.2, 0.2, 0.2) # Specular color
data += struct.pack('<f', 50.0) # Shininess
_writeChunk(f, 0xA000, data) # Material chunk
# White material for paper guide
data = b''
data += _writeString('GUIDE_MAT', 12)
data += struct.pack('<fff', 0.9, 0.9, 0.9) # Diffuse color (white)
data += struct.pack('<fff', 0.8, 0.8, 0.8) # Ambient color
data += struct.pack('<fff', 0.1, 0.1, 0.1) # Specular color
data += struct.pack('<f', 30.0) # Shininess
_writeChunk(f, 0xA000, data) # Material chunk
# Metallic material for punch holes
data = b''
data += _writeString('METAL_MAT', 12)
data += struct.pack('<fff', 0.6, 0.6, 0.6) # Diffuse color (metallic gray)
data += struct.pack('<fff', 0.2, 0.2, 0.2) # Ambient color
data += struct.pack('<fff', 0.9, 0.9, 0.9) # Specular color
data += struct.pack('<f', 200.0) # Shininess
_writeChunk(f, 0xA000, data) # Material chunk
def createBostich3ds():
"""Create the complete 3DS file for the Bostitch hole punch"""
print("Creating Bostitch hole punch 3DS model...")
# Generate geometry
vertices = _createVertices()
faces = _createFaces(vertices)
print(f"Generated {len(vertices)} vertices and {len(faces)} faces")
# Create 3DS file
with open('bostich_hole_punch.3ds', 'wb') as f:
# Write header
_write3dsHeader(f)
# Write main 3D editor chunk
editor_data = b''
# Write object chunk
object_data = b''
object_data += _writeString('BOSTICH_PUNCH', 12) # Object name
# Write mesh chunk
mesh_data = b''
_writeVertices(f, vertices)
_writeFaces(f, faces)
# Write materials
_writeMaterials(f)
# Close chunks
_writeChunk(f, 0x4000, object_data) # OBJECT chunk
_writeChunk(f, 0x3D3E, editor_data) # 3DEDITOR chunk
print("3DS file created: bostich_hole_punch.3ds")
print("Dimensions: 20cm x 10cm x 10cm")
print("Materials: Dark blue lever, light gray base, white paper guide, metallic punch holes")
if __name__ == "__main__":
createBostich3ds()