266 lines
9.2 KiB
Python
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()
|