<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Stl on Backend Engineering Strategy Tools</title><link>https://backend-engineering-strategy-tools.github.io/site/tags/stl/</link><description>Recent content in Stl on Backend Engineering Strategy Tools</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Tue, 02 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://backend-engineering-strategy-tools.github.io/site/tags/stl/index.xml" rel="self" type="application/rss+xml"/><item><title>Blender Python — Procedural Mesh for 3D Printing</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/frameworks-tools/blender-python/</link><pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/frameworks-tools/blender-python/</guid><description>&lt;p&gt;Write a Python script that builds geometry programmatically using Blender&amp;rsquo;s &lt;code&gt;bpy&lt;/code&gt; API, then export as STL. No manual modelling — the script is the source of truth, re-running it regenerates everything.&lt;/p&gt;
&lt;p&gt;This pattern is useful when you have a family of similar parts (different sizes, repeated structures, parametric variations) or when the geometry is defined by rules rather than artistic decisions.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="when-to-use-this-vs-alternatives"&gt;When to use this vs. alternatives
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;Blender Python&lt;/th&gt;
 &lt;th&gt;OpenSCAD&lt;/th&gt;
 &lt;th&gt;CadQuery&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Booleans, extrude, modifiers&lt;/td&gt;
 &lt;td&gt;✓ built-in&lt;/td&gt;
 &lt;td&gt;✓ CSG only&lt;/td&gt;
 &lt;td&gt;limited&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Sculpt / organic shapes&lt;/td&gt;
 &lt;td&gt;✓&lt;/td&gt;
 &lt;td&gt;✗&lt;/td&gt;
 &lt;td&gt;✗&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Parametric constraints&lt;/td&gt;
 &lt;td&gt;manual&lt;/td&gt;
 &lt;td&gt;manual&lt;/td&gt;
 &lt;td&gt;✓ strong&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Python ecosystem&lt;/td&gt;
 &lt;td&gt;✓ full stdlib&lt;/td&gt;
 &lt;td&gt;✗ own language&lt;/td&gt;
 &lt;td&gt;✓&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Interactive viewport preview&lt;/td&gt;
 &lt;td&gt;✓&lt;/td&gt;
 &lt;td&gt;✗&lt;/td&gt;
 &lt;td&gt;✗&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Export to STL&lt;/td&gt;
 &lt;td&gt;✓ one call&lt;/td&gt;
 &lt;td&gt;✓&lt;/td&gt;
 &lt;td&gt;✓&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For repetitive mechanical geometry with booleans (holes, sockets, cutouts), Blender Python is the fastest path if you already know Python. The interactive viewport lets you catch geometry problems before exporting.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="core-pattern"&gt;Core pattern
&lt;/h2&gt;&lt;p&gt;Every script follows the same structure:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; bpy&lt;span style="color:#f92672"&gt;,&lt;/span&gt; bmesh&lt;span style="color:#f92672"&gt;,&lt;/span&gt; math&lt;span style="color:#f92672"&gt;,&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 1. Create a primitive — it becomes bpy.context.object&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mesh&lt;span style="color:#f92672"&gt;.&lt;/span&gt;primitive_cylinder_add(vertices&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;64&lt;/span&gt;, radius&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;, depth&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;obj &lt;span style="color:#f92672"&gt;=&lt;/span&gt; bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;context&lt;span style="color:#f92672"&gt;.&lt;/span&gt;object
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 2. Edit vertices directly via bmesh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;object&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mode_set(mode&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;EDIT&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bm &lt;span style="color:#f92672"&gt;=&lt;/span&gt; bmesh&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_edit_mesh(obj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; v &lt;span style="color:#f92672"&gt;in&lt;/span&gt; bm&lt;span style="color:#f92672"&gt;.&lt;/span&gt;verts:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; v&lt;span style="color:#f92672"&gt;.&lt;/span&gt;co&lt;span style="color:#f92672"&gt;.&lt;/span&gt;z &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; v&lt;span style="color:#f92672"&gt;.&lt;/span&gt;co&lt;span style="color:#f92672"&gt;.&lt;/span&gt;x &lt;span style="color:#f92672"&gt;*=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.9&lt;/span&gt; &lt;span style="color:#75715e"&gt;# taper the top&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bmesh&lt;span style="color:#f92672"&gt;.&lt;/span&gt;update_edit_mesh(obj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;object&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mode_set(mode&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;OBJECT&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 3. Boolean modifier to cut a hole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mesh&lt;span style="color:#f92672"&gt;.&lt;/span&gt;primitive_cylinder_add(radius&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, depth&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3.2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cutter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;context&lt;span style="color:#f92672"&gt;.&lt;/span&gt;object
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mod &lt;span style="color:#f92672"&gt;=&lt;/span&gt; obj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;modifiers&lt;span style="color:#f92672"&gt;.&lt;/span&gt;new(&lt;span style="color:#e6db74"&gt;&amp;#34;Hole&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;BOOLEAN&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mod&lt;span style="color:#f92672"&gt;.&lt;/span&gt;object &lt;span style="color:#f92672"&gt;=&lt;/span&gt; cutter
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mod&lt;span style="color:#f92672"&gt;.&lt;/span&gt;operation &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;DIFFERENCE&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;context&lt;span style="color:#f92672"&gt;.&lt;/span&gt;view_layer&lt;span style="color:#f92672"&gt;.&lt;/span&gt;objects&lt;span style="color:#f92672"&gt;.&lt;/span&gt;active &lt;span style="color:#f92672"&gt;=&lt;/span&gt; obj
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;object&lt;span style="color:#f92672"&gt;.&lt;/span&gt;modifier_apply(modifier&lt;span style="color:#f92672"&gt;=&lt;/span&gt;mod&lt;span style="color:#f92672"&gt;.&lt;/span&gt;name)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data&lt;span style="color:#f92672"&gt;.&lt;/span&gt;objects&lt;span style="color:#f92672"&gt;.&lt;/span&gt;remove(cutter, do_unlink&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 4. Export&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;object&lt;span style="color:#f92672"&gt;.&lt;/span&gt;select_all(action&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;DESELECT&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;obj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;select_set(&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;wm&lt;span style="color:#f92672"&gt;.&lt;/span&gt;stl_export(filepath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/tmp/part.stl&amp;#34;&lt;/span&gt;, export_selected_objects&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Build complexity by wrapping each operation in a function, then calling it in a loop over a size or parameter list.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="running-the-script"&gt;Running the script
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Open Blender → switch to the &lt;strong&gt;Scripting&lt;/strong&gt; workspace&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;New&lt;/strong&gt; or &lt;strong&gt;Open&lt;/strong&gt; to load your &lt;code&gt;.py&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Run Script&lt;/strong&gt; (▶) or press &lt;code&gt;Alt + P&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Output and errors appear in the system console (&lt;code&gt;Window → Toggle System Console&lt;/code&gt; on Windows, or launch Blender from a terminal on macOS/Linux).&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="key-api-surface"&gt;Key API surface
&lt;/h2&gt;&lt;h3 id="primitives"&gt;Primitives
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mesh&lt;span style="color:#f92672"&gt;.&lt;/span&gt;primitive_cylinder_add(vertices&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;32&lt;/span&gt;, radius&lt;span style="color:#f92672"&gt;=&lt;/span&gt;r, depth&lt;span style="color:#f92672"&gt;=&lt;/span&gt;h)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mesh&lt;span style="color:#f92672"&gt;.&lt;/span&gt;primitive_cube_add(size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;s)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mesh&lt;span style="color:#f92672"&gt;.&lt;/span&gt;primitive_plane_add(size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;s)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All primitives land at the world origin and become &lt;code&gt;bpy.context.object&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="transforms"&gt;Transforms
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;obj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;scale &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (sx, sy, sz)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;obj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;location &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (x, y, z)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;obj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rotation_euler &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (rx, ry, rz) &lt;span style="color:#75715e"&gt;# radians&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;object&lt;span style="color:#f92672"&gt;.&lt;/span&gt;transform_apply(scale&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, location&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;, rotation&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Apply transforms before any bmesh edits — otherwise vertex coordinates are in local (pre-scale) space, and your edit positions won&amp;rsquo;t match world coordinates.&lt;/p&gt;
&lt;h3 id="bmesh-direct-vertex--edge--face-editing"&gt;bmesh (direct vertex / edge / face editing)
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;object&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mode_set(mode&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;EDIT&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bm &lt;span style="color:#f92672"&gt;=&lt;/span&gt; bmesh&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_edit_mesh(obj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; v &lt;span style="color:#f92672"&gt;in&lt;/span&gt; bm&lt;span style="color:#f92672"&gt;.&lt;/span&gt;verts:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; v&lt;span style="color:#f92672"&gt;.&lt;/span&gt;co&lt;span style="color:#f92672"&gt;.&lt;/span&gt;z &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; v&lt;span style="color:#f92672"&gt;.&lt;/span&gt;co&lt;span style="color:#f92672"&gt;.&lt;/span&gt;x &lt;span style="color:#f92672"&gt;*=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.9&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bmesh&lt;span style="color:#f92672"&gt;.&lt;/span&gt;update_edit_mesh(obj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;object&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mode_set(mode&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;OBJECT&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="boolean-modifiers"&gt;Boolean modifiers
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mod &lt;span style="color:#f92672"&gt;=&lt;/span&gt; target&lt;span style="color:#f92672"&gt;.&lt;/span&gt;modifiers&lt;span style="color:#f92672"&gt;.&lt;/span&gt;new(&lt;span style="color:#e6db74"&gt;&amp;#34;Name&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;BOOLEAN&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mod&lt;span style="color:#f92672"&gt;.&lt;/span&gt;object &lt;span style="color:#f92672"&gt;=&lt;/span&gt; cutter
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mod&lt;span style="color:#f92672"&gt;.&lt;/span&gt;operation &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;DIFFERENCE&amp;#39;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# or UNION or INTERSECT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mod&lt;span style="color:#f92672"&gt;.&lt;/span&gt;solver &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;EXACT&amp;#39;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# more reliable than FAST for tight geometry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;context&lt;span style="color:#f92672"&gt;.&lt;/span&gt;view_layer&lt;span style="color:#f92672"&gt;.&lt;/span&gt;objects&lt;span style="color:#f92672"&gt;.&lt;/span&gt;active &lt;span style="color:#f92672"&gt;=&lt;/span&gt; target
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;object&lt;span style="color:#f92672"&gt;.&lt;/span&gt;modifier_apply(modifier&lt;span style="color:#f92672"&gt;=&lt;/span&gt;mod&lt;span style="color:#f92672"&gt;.&lt;/span&gt;name)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data&lt;span style="color:#f92672"&gt;.&lt;/span&gt;objects&lt;span style="color:#f92672"&gt;.&lt;/span&gt;remove(cutter, do_unlink&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Apply and remove the cutter immediately — leaving stale cutter objects causes confusion on subsequent runs.&lt;/p&gt;
&lt;h3 id="joining-objects-single-boolean-cut-for-a-grid-of-holes"&gt;Joining objects (single boolean cut for a grid of holes)
&lt;/h3&gt;&lt;p&gt;Rather than applying one boolean per hole, join all cutters first:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; obj &lt;span style="color:#f92672"&gt;in&lt;/span&gt; cyl_objects:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; obj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;select_set(&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;context&lt;span style="color:#f92672"&gt;.&lt;/span&gt;view_layer&lt;span style="color:#f92672"&gt;.&lt;/span&gt;objects&lt;span style="color:#f92672"&gt;.&lt;/span&gt;active &lt;span style="color:#f92672"&gt;=&lt;/span&gt; cyl_objects[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;object&lt;span style="color:#f92672"&gt;.&lt;/span&gt;join()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cutter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;context&lt;span style="color:#f92672"&gt;.&lt;/span&gt;active_object
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# now do one boolean cut on the plate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One boolean operation is faster and produces cleaner topology than N serial cuts.&lt;/p&gt;
&lt;h3 id="stl-export-blender-4x--5x"&gt;STL export (Blender 4.x / 5.x)
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;wm&lt;span style="color:#f92672"&gt;.&lt;/span&gt;stl_export(filepath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/abs/path/part.stl&amp;#34;&lt;/span&gt;, export_selected_objects&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="collections-organising-multi-part-output"&gt;Collections (organising multi-part output)
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;col &lt;span style="color:#f92672"&gt;=&lt;/span&gt; bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data&lt;span style="color:#f92672"&gt;.&lt;/span&gt;collections&lt;span style="color:#f92672"&gt;.&lt;/span&gt;new(&lt;span style="color:#e6db74"&gt;&amp;#34;Round Bases&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;context&lt;span style="color:#f92672"&gt;.&lt;/span&gt;scene&lt;span style="color:#f92672"&gt;.&lt;/span&gt;collection&lt;span style="color:#f92672"&gt;.&lt;/span&gt;children&lt;span style="color:#f92672"&gt;.&lt;/span&gt;link(col)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; c &lt;span style="color:#f92672"&gt;in&lt;/span&gt; obj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;users_collection:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; c&lt;span style="color:#f92672"&gt;.&lt;/span&gt;objects&lt;span style="color:#f92672"&gt;.&lt;/span&gt;unlink(obj)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;col&lt;span style="color:#f92672"&gt;.&lt;/span&gt;objects&lt;span style="color:#f92672"&gt;.&lt;/span&gt;link(obj)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="parametric-families"&gt;Parametric families
&lt;/h2&gt;&lt;p&gt;The main loop pattern — build one function that takes dimensions, call it for each size:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SIZES &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [&lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;15&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;25&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; w &lt;span style="color:#f92672"&gt;in&lt;/span&gt; SIZES:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; obj &lt;span style="color:#f92672"&gt;=&lt;/span&gt; make_clip(width&lt;span style="color:#f92672"&gt;=&lt;/span&gt;w)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; obj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;name &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;clip_&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;w&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;mm&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;object&lt;span style="color:#f92672"&gt;.&lt;/span&gt;select_all(action&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;DESELECT&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; obj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;select_set(&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;wm&lt;span style="color:#f92672"&gt;.&lt;/span&gt;stl_export(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; filepath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/output/clip_&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;w&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;mm.stl&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; export_selected_objects&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Put all tuneable values at the top of the file in a &lt;code&gt;# CONFIG&lt;/code&gt; block. This makes the script easy to hand to Claude and say &amp;ldquo;change the hole diameter to 7mm and add two more rows.&amp;rdquo;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="clearing-the-scene-before-a-run"&gt;Clearing the scene before a run
&lt;/h2&gt;&lt;p&gt;Add this at the top when iterating interactively — otherwise re-running doubles the objects:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;object&lt;span style="color:#f92672"&gt;.&lt;/span&gt;select_all(action&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;SELECT&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ops&lt;span style="color:#f92672"&gt;.&lt;/span&gt;object&lt;span style="color:#f92672"&gt;.&lt;/span&gt;delete()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; block &lt;span style="color:#f92672"&gt;in&lt;/span&gt; bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data&lt;span style="color:#f92672"&gt;.&lt;/span&gt;meshes:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bpy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data&lt;span style="color:#f92672"&gt;.&lt;/span&gt;meshes&lt;span style="color:#f92672"&gt;.&lt;/span&gt;remove(block)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="viewport-layout-before-export"&gt;Viewport layout before export
&lt;/h2&gt;&lt;p&gt;Spread objects out so you can visually review them before committing to an export:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;xoff &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; obj, w &lt;span style="color:#f92672"&gt;in&lt;/span&gt; zip(objects, widths):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; obj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;location&lt;span style="color:#f92672"&gt;.&lt;/span&gt;x &lt;span style="color:#f92672"&gt;=&lt;/span&gt; xoff
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; xoff &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; w &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For print batches, sort largest-first and pack into rows within your bed dimensions (shelf bin-packing against &lt;code&gt;PLATE_W × PLATE_H&lt;/code&gt;).&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="working-with-claude"&gt;Working with Claude
&lt;/h2&gt;&lt;p&gt;The config-block pattern pairs well with LLM iteration. Because all tuneable values sit in one place and each operation is a named function, you can describe changes in plain language:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;Change &lt;code&gt;HOLE_DIAM&lt;/code&gt; to 6 and add a third text line below line 2&amp;rdquo;&lt;/em&gt; — Claude edits two constants and adds a &lt;code&gt;make_line()&lt;/code&gt; call.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;The holes in rows 2 and 3 need 3mm more clearance from the edge&amp;rdquo;&lt;/em&gt; — Claude adjusts &lt;code&gt;x_origin&lt;/code&gt; offset computation.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;Add a chamfer around the perimeter of the top face&amp;rdquo;&lt;/em&gt; — Claude adds a bmesh loop and inset operation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The workflow is: run → look at viewport → describe the problem → apply updated script → repeat. Iteration is fast because the viewport gives immediate visual feedback and the script regenerates from scratch each run.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="limitations"&gt;Limitations
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Booleans are fragile on non-manifold geometry.&lt;/strong&gt; If a cutter face is coplanar with the target, or vertices are nearly coincident, Blender&amp;rsquo;s solver can produce garbage. Add a small bleed (0.5–1 mm) so cutters fully penetrate surfaces.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No parametric constraints.&lt;/strong&gt; Unlike CadQuery, there is no &amp;ldquo;keep this face parallel to that face&amp;rdquo; system. Dimension changes cascade manually. This is manageable when the config block is the only place numbers live.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Script state accumulates.&lt;/strong&gt; Re-running in an existing scene doubles the objects. Clear the scene first (see above) or check for existing objects by name before creating.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="related"&gt;Related
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/garage/" &gt;Garage — Scripted Parts&lt;/a&gt; — hole box and other physical builds using this approach&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/rack-support-brace/" &gt;Rack Support Brace&lt;/a&gt; — step-by-step: script → renders → headless → CI/CD&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>