ambi-recurve-limb-flat.py raw
1 """
2 Flat limb strip cutout — the 316L sheet blank before any bending.
3 A tapered hexagon, widest at center, symmetric taper to tips.
4 3 bolt holes at center. Thickness from sheet stock (3.5mm at center
5 tapering to 1.4mm at tips — but this is a flat cutout so we use
6 a single sheet thickness and show the WIDTH taper only).
7
8 Run in Blender: Scripting workspace > Open > Run Script
9 """
10
11 import bpy
12 import bmesh
13 import math
14
15 def clear_scene():
16 bpy.ops.object.select_all(action='SELECT')
17 bpy.ops.object.delete()
18
19 def make_material(name, color, metallic=0.0, roughness=0.5):
20 mat = bpy.data.materials.new(name)
21 mat.use_nodes = True
22 bsdf = mat.node_tree.nodes["Principled BSDF"]
23 bsdf.inputs["Base Color"].default_value = (*color, 1.0)
24 bsdf.inputs["Metallic"].default_value = metallic
25 bsdf.inputs["Roughness"].default_value = roughness
26 return mat
27
28 def main():
29 clear_scene()
30
31 steel = make_material("316L_FullHard", (0.08, 0.08, 0.08), metallic=0.8, roughness=0.4)
32
33 # Taper profile from doc (distance from center in mm, half-width in mm)
34 # Symmetric — we define one side and mirror.
35 # Center: 44mm wide (half = 22)
36 # 140mm: 40mm wide (half = 20)
37 # 250mm: 32mm wide (half = 16)
38 # 350mm: 24mm wide (half = 12)
39 # 500mm: 20mm wide (half = 10)
40 #
41 # Thickness taper (for the Z extrusion at each section):
42 # Center: 3.5mm
43 # 140mm: 3.0mm
44 # 250mm: 2.2mm
45 # 350mm: 1.8mm
46 # 500mm: 1.4mm
47
48 # Profile points: (y_mm, half_width_mm, thickness_mm)
49 profile = [
50 (0, 22.0, 3.5),
51 (50, 21.5, 3.35),
52 (100, 20.8, 3.15),
53 (140, 20.0, 3.0),
54 (180, 18.4, 2.7),
55 (220, 17.2, 2.4),
56 (250, 16.0, 2.2),
57 (280, 14.6, 2.05),
58 (310, 13.2, 1.9),
59 (350, 12.0, 1.8),
60 (380, 11.2, 1.7),
61 (420, 10.6, 1.55),
62 (460, 10.2, 1.45),
63 (500, 10.0, 1.4),
64 ]
65
66 # Build mesh: for each Y station, create a rectangular cross-section
67 # (width varies, thickness varies). Full strip is symmetric about Y=0.
68 bm = bmesh.new()
69
70 # Build both halves: negative Y then positive Y
71 all_stations = []
72 for y_mm, hw_mm, th_mm in reversed(profile):
73 if y_mm > 0:
74 all_stations.append((-y_mm, hw_mm, th_mm))
75 for y_mm, hw_mm, th_mm in profile:
76 all_stations.append((y_mm, hw_mm, th_mm))
77
78 # Scale to meters
79 for y_mm, hw_mm, th_mm in all_stations:
80 y = y_mm / 1000.0
81 hw = hw_mm / 1000.0
82 th = th_mm / 1000.0
83
84 # Four corners of cross-section at this Y station
85 bm.verts.new((-hw, y, 0))
86 bm.verts.new(( hw, y, 0))
87 bm.verts.new(( hw, y, th))
88 bm.verts.new((-hw, y, th))
89
90 bm.verts.ensure_lookup_table()
91 n = len(all_stations)
92
93 # Side faces
94 for i in range(n - 1):
95 base = i * 4
96 for j in range(4):
97 v0 = bm.verts[base + j]
98 v1 = bm.verts[base + (j + 1) % 4]
99 v2 = bm.verts[base + 4 + (j + 1) % 4]
100 v3 = bm.verts[base + 4 + j]
101 bm.faces.new([v0, v1, v2, v3])
102
103 # Cap ends
104 bm.faces.new([bm.verts[0], bm.verts[1], bm.verts[2], bm.verts[3]])
105 last = (n - 1) * 4
106 bm.faces.new([bm.verts[last+3], bm.verts[last+2], bm.verts[last+1], bm.verts[last]])
107
108 mesh = bpy.data.meshes.new("Limb_Strip_Flat")
109 bm.to_mesh(mesh)
110 bm.free()
111
112 obj = bpy.data.objects.new("Limb_Strip_Flat", mesh)
113 bpy.context.scene.collection.objects.link(obj)
114 obj.data.materials.append(steel)
115
116 # Smooth shading
117 for f in obj.data.polygons:
118 f.use_smooth = True
119
120 # Subdivision for smoothness
121 mod = obj.modifiers.new("Subsurf", 'SUBSURF')
122 mod.levels = 2
123 mod.render_levels = 3
124
125 # --- Bolt holes (3x M6, 30mm spacing at center) ---
126 for offset_mm in [-30, 0, 30]:
127 bpy.ops.mesh.primitive_cylinder_add(
128 radius=0.00325, # 6.5mm clearance hole / 2
129 depth=0.006, # through the thickness
130 location=(0, offset_mm / 1000.0, 0.00175))
131 hole = bpy.context.active_object
132 hole.name = f"BoltHole_{offset_mm}"
133
134 # Boolean difference
135 mod = obj.modifiers.new(f"Hole_{offset_mm}", 'BOOLEAN')
136 mod.operation = 'DIFFERENCE'
137 mod.object = hole
138 bpy.context.view_layer.objects.active = obj
139 bpy.ops.object.modifier_apply(modifier=f"Hole_{offset_mm}")
140 bpy.data.objects.remove(hole)
141
142 bpy.ops.object.select_all(action='DESELECT')
143 obj.select_set(True)
144 bpy.context.view_layer.objects.active = obj
145
146 print("Flat limb strip cutout created.")
147 print("1000mm total length, tapered width (44mm center -> 20mm tips),")
148 print("tapered thickness (3.5mm center -> 1.4mm tips), 3x M6 bolt holes.")
149 print("This is the blank before press-brake recurve forming.")
150
151 if __name__ == "__main__":
152 main()
153