|
| 1 | +import math |
| 2 | +import numpy as np |
| 3 | +from stl import mesh # Requires numpy-stl package: pip install numpy-stl |
| 4 | +import struct |
| 5 | + |
| 6 | +def create_rocket_stl(filename="rocket_model.stl", length=100, radius=10): |
| 7 | + """ |
| 8 | + Creates a detailed rocket-shaped STL file with proper scale. |
| 9 | + |
| 10 | + Parameters |
| 11 | + ---------- |
| 12 | + filename : str |
| 13 | + Output filename |
| 14 | + length : float |
| 15 | + Rocket length in meters |
| 16 | + radius : float |
| 17 | + Rocket base radius in meters |
| 18 | + """ |
| 19 | + |
| 20 | + # More realistic proportions |
| 21 | + nose_length = length * 0.25 |
| 22 | + body_length = length * 0.65 |
| 23 | + engine_length = length * 0.1 |
| 24 | + |
| 25 | + # Fin parameters |
| 26 | + fin_height = radius * 3 |
| 27 | + fin_width = radius * 2 |
| 28 | + fin_thickness = radius * 0.2 |
| 29 | + num_fins = 4 |
| 30 | + |
| 31 | + # Calculate number of vertices for smooth curves |
| 32 | + num_segments = 36 |
| 33 | + vertices = [] |
| 34 | + faces = [] |
| 35 | + |
| 36 | + # Helper function to add triangle |
| 37 | + def add_triangle(v1, v2, v3): |
| 38 | + nonlocal faces |
| 39 | + idx = len(vertices) |
| 40 | + vertices.extend([v1, v2, v3]) |
| 41 | + faces.append([idx, idx+1, idx+2]) |
| 42 | + |
| 43 | + # Helper function to add quadrilateral as two triangles |
| 44 | + def add_quad(v1, v2, v3, v4): |
| 45 | + add_triangle(v1, v2, v3) |
| 46 | + add_triangle(v1, v3, v4) |
| 47 | + |
| 48 | + # 1. Create nose cone (pointed ogive) |
| 49 | + nose_vertices = [] |
| 50 | + for i in range(num_segments + 1): |
| 51 | + angle = 2 * math.pi * i / num_segments |
| 52 | + x = math.cos(angle) * radius |
| 53 | + y = math.sin(angle) * radius |
| 54 | + z = length # Tip at full length |
| 55 | + nose_vertices.append([x, y, z]) |
| 56 | + |
| 57 | + # Create nose cone triangles |
| 58 | + nose_tip = [0, 0, length] |
| 59 | + for i in range(num_segments): |
| 60 | + v1 = nose_tip |
| 61 | + v2 = nose_vertices[i] |
| 62 | + v3 = nose_vertices[(i + 1) % num_segments] |
| 63 | + add_triangle(v1, v2, v3) |
| 64 | + |
| 65 | + # 2. Create main body cylinder |
| 66 | + body_z_start = length - nose_length |
| 67 | + body_z_end = engine_length |
| 68 | + |
| 69 | + # Create vertices for top and bottom circles of body |
| 70 | + top_circle = [] |
| 71 | + bottom_circle = [] |
| 72 | + |
| 73 | + for i in range(num_segments): |
| 74 | + angle = 2 * math.pi * i / num_segments |
| 75 | + x = math.cos(angle) * radius |
| 76 | + y = math.sin(angle) * radius |
| 77 | + |
| 78 | + top_circle.append([x, y, body_z_start]) |
| 79 | + bottom_circle.append([x, y, body_z_end]) |
| 80 | + |
| 81 | + # Create body cylinder triangles |
| 82 | + for i in range(num_segments): |
| 83 | + next_i = (i + 1) % num_segments |
| 84 | + |
| 85 | + # Side quad |
| 86 | + v1 = top_circle[i] |
| 87 | + v2 = top_circle[next_i] |
| 88 | + v3 = bottom_circle[next_i] |
| 89 | + v4 = bottom_circle[i] |
| 90 | + add_quad(v1, v2, v3, v4) |
| 91 | + |
| 92 | + # Top circle (connecting to nose) |
| 93 | + v1 = top_circle[i] |
| 94 | + v2 = top_circle[next_i] |
| 95 | + v3 = nose_vertices[i] |
| 96 | + add_triangle(v1, v2, v3) |
| 97 | + |
| 98 | + # 3. Create engine nozzle (truncated cone) |
| 99 | + engine_radius = radius * 0.7 |
| 100 | + engine_z_end = 0 |
| 101 | + |
| 102 | + # Create vertices for engine circles |
| 103 | + engine_top_circle = [] |
| 104 | + engine_bottom_circle = [] |
| 105 | + |
| 106 | + for i in range(num_segments): |
| 107 | + angle = 2 * math.pi * i / num_segments |
| 108 | + x_top = math.cos(angle) * radius |
| 109 | + y_top = math.sin(angle) * radius |
| 110 | + x_bottom = math.cos(angle) * engine_radius |
| 111 | + y_bottom = math.sin(angle) * engine_radius |
| 112 | + |
| 113 | + engine_top_circle.append([x_top, y_top, body_z_end]) |
| 114 | + engine_bottom_circle.append([x_bottom, y_bottom, engine_z_end]) |
| 115 | + |
| 116 | + # Create engine nozzle triangles |
| 117 | + for i in range(num_segments): |
| 118 | + next_i = (i + 1) % num_segments |
| 119 | + |
| 120 | + # Side quad |
| 121 | + v1 = engine_top_circle[i] |
| 122 | + v2 = engine_top_circle[next_i] |
| 123 | + v3 = engine_bottom_circle[next_i] |
| 124 | + v4 = engine_bottom_circle[i] |
| 125 | + add_quad(v1, v2, v3, v4) |
| 126 | + |
| 127 | + # Connect to body |
| 128 | + v1 = engine_top_circle[i] |
| 129 | + v2 = engine_top_circle[next_i] |
| 130 | + v3 = bottom_circle[i] |
| 131 | + add_triangle(v1, v2, v3) |
| 132 | + |
| 133 | + # 4. Create fins |
| 134 | + for fin_num in range(num_fins): |
| 135 | + fin_angle = 2 * math.pi * fin_num / num_fins |
| 136 | + |
| 137 | + # Fin vertices |
| 138 | + fin_base_center = [0, 0, body_z_end * 0.5] |
| 139 | + |
| 140 | + # Inner fin edge (attached to rocket) |
| 141 | + inner_x = math.cos(fin_angle) * radius |
| 142 | + inner_y = math.sin(fin_angle) * radius |
| 143 | + inner_top = [inner_x, inner_y, body_z_end + fin_height * 0.3] |
| 144 | + inner_bottom = [inner_x, inner_y, body_z_end - fin_height * 0.7] |
| 145 | + |
| 146 | + # Outer fin edge |
| 147 | + outer_x = math.cos(fin_angle) * (radius + fin_width) |
| 148 | + outer_y = math.sin(fin_angle) * (radius + fin_width) |
| 149 | + outer_top = [outer_x, outer_y, body_z_end + fin_height * 0.3] |
| 150 | + outer_bottom = [outer_x, outer_y, body_z_end - fin_height * 0.7] |
| 151 | + |
| 152 | + # Fin side that attaches to rocket |
| 153 | + add_quad(inner_top, inner_bottom, bottom_circle[fin_num * num_segments // num_fins], |
| 154 | + bottom_circle[(fin_num * num_segments // num_fins + 1) % num_segments]) |
| 155 | + |
| 156 | + # Fin surfaces |
| 157 | + # Front face |
| 158 | + add_quad(inner_top, outer_top, outer_bottom, inner_bottom) |
| 159 | + |
| 160 | + # Top face |
| 161 | + add_triangle(inner_top, outer_top, |
| 162 | + [inner_top[0] + math.cos(fin_angle + math.pi/2) * fin_thickness * 0.5, |
| 163 | + inner_top[1] + math.sin(fin_angle + math.pi/2) * fin_thickness * 0.5, |
| 164 | + inner_top[2]]) |
| 165 | + |
| 166 | + # Bottom face |
| 167 | + add_triangle(inner_bottom, outer_bottom, |
| 168 | + [inner_bottom[0] + math.cos(fin_angle + math.pi/2) * fin_thickness * 0.5, |
| 169 | + inner_bottom[1] + math.sin(fin_angle + math.pi/2) * fin_thickness * 0.5, |
| 170 | + inner_bottom[2]]) |
| 171 | + |
| 172 | + # 5. Create bottom cap |
| 173 | + center_bottom = [0, 0, engine_z_end] |
| 174 | + for i in range(num_segments): |
| 175 | + next_i = (i + 1) % num_segments |
| 176 | + add_triangle(center_bottom, engine_bottom_circle[i], engine_bottom_circle[next_i]) |
| 177 | + |
| 178 | + # Convert to numpy arrays |
| 179 | + vertices_array = np.array(vertices) |
| 180 | + faces_array = np.array(faces) |
| 181 | + |
| 182 | + # Create the mesh |
| 183 | + rocket_mesh = mesh.Mesh(np.zeros(faces_array.shape[0], dtype=mesh.Mesh.dtype)) |
| 184 | + for i, face in enumerate(faces_array): |
| 185 | + for j in range(3): |
| 186 | + rocket_mesh.vectors[i][j] = vertices_array[face[j]] |
| 187 | + |
| 188 | + # Write the mesh to file |
| 189 | + rocket_mesh.save(filename) |
| 190 | + print(f"Rocket STL saved to {filename}") |
| 191 | + print(f"Total triangles: {len(faces)}") |
| 192 | + |
| 193 | +# Alternative version using pure Python STL generation |
| 194 | +def create_rocket_stl_simple(filename="rocket_simple.stl", length=100, radius=10): |
| 195 | + """Simpler version using pure Python without numpy-stl dependency""" |
| 196 | + |
| 197 | + def write_stl(faces, filename): |
| 198 | + with open(filename, 'wb') as f: |
| 199 | + # Write 80 byte header |
| 200 | + f.write(b'\x00' * 80) |
| 201 | + # Write number of faces |
| 202 | + f.write(struct.pack('<I', len(faces))) |
| 203 | + |
| 204 | + for face in faces: |
| 205 | + # Write normal (dummy, will be recalculated) |
| 206 | + f.write(struct.pack('<fff', 0.0, 0.0, 0.0)) |
| 207 | + # Write three vertices |
| 208 | + for vertex in face: |
| 209 | + f.write(struct.pack('<fff', vertex[0], vertex[1], vertex[2])) |
| 210 | + # Write attribute byte count |
| 211 | + f.write(struct.pack('<H', 0)) |
| 212 | + |
| 213 | + # Simplified geometry - create a more detailed rocket than original but simpler than full version |
| 214 | + num_segments = 16 |
| 215 | + faces = [] |
| 216 | + |
| 217 | + # Add more triangles for better shape... |
| 218 | + # (You can combine the face generation logic from above with this write_stl function) |
| 219 | + |
| 220 | + write_stl(faces, filename) |
0 commit comments