<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>3d-Printing on Backend Engineering Strategy Tools</title><link>https://backend-engineering-strategy-tools.github.io/site/tags/3d-printing/</link><description>Recent content in 3d-Printing on Backend Engineering Strategy Tools</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Wed, 03 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://backend-engineering-strategy-tools.github.io/site/tags/3d-printing/index.xml" rel="self" type="application/rss+xml"/><item><title>Optics — Can We Print a Lens?</title><link>https://backend-engineering-strategy-tools.github.io/site/garage/optics-lens/</link><pubDate>Wed, 03 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/garage/optics-lens/</guid><description>&lt;p&gt;The question: can a 3D printed part function as a lens? Not a perfect optical instrument — but something that focuses or diffuses light usefully.&lt;/p&gt;
&lt;p&gt;A Fresnel lens is the natural starting point. Instead of a thick curved lens, a Fresnel collapses the profile into a flat plate with concentric stepped rings — each ring approximating the angle of the equivalent curved surface. Much thinner, printable in flat layers.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-1--print-and-test"&gt;Step 1 — Print and test
&lt;/h2&gt;&lt;p&gt;Two approaches to try:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;FDM — clear PLA&lt;/strong&gt;
Printed on the Kobra X. Surface finish from FDM is rough at the layer scale — likely too much scatter for a clean focus, but worth seeing what you actually get.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Resin — clear resins&lt;/strong&gt;
Better surface resolution and the material can be optically transparent after curing and polishing. More promising. Several clear resins ordered for this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ABS-Like 2.0 Clear — structural, less brittle&lt;/li&gt;
&lt;li&gt;Standard Clear — higher detail&lt;/li&gt;
&lt;li&gt;High Clear / Transparent resin&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/garage/3d-printing/" &gt;3D Printing&lt;/a&gt; for the full resin inventory.&lt;/p&gt;
&lt;p&gt;Not started. Print attempts and results to follow.&lt;/p&gt;
&lt;hr&gt;</description></item><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><item><title>Procedural Mesh — Blender Python &amp; AI-Assisted Geometry</title><link>https://backend-engineering-strategy-tools.github.io/site/projects/procedural-mesh/</link><pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/projects/procedural-mesh/</guid><description>&lt;p&gt;Using Python scripts inside Blender to generate, manipulate, and export geometry — rather than modelling by hand. The config block is the model. Changing a dimension means editing a constant and re-running, not touching a mesh.&lt;/p&gt;
&lt;p&gt;Two related but distinct approaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Parametric generation&lt;/strong&gt; — the script builds geometry from scratch. Holes, plates, text engravings, export to STL, automated renders. The shape lives entirely in code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mesh manipulation&lt;/strong&gt; — an existing STL is imported and cut up. Different problem: no parametric handle, working with whatever the downloaded mesh gives you.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both involve AI in the loop — either iterating on the Python with Claude, or using AI to determine cut planes on existing geometry.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="stages"&gt;Stages
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Step&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;Status&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;0&lt;/td&gt;
 &lt;td&gt;Manual Blender modelling — &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/garage/scout-badges-resin/" &gt;Packat &amp;amp; Klart badge&lt;/a&gt; and &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/garage/casting-badges/" &gt;scout emblem casting master&lt;/a&gt;; learning the tool before scripting&lt;/td&gt;
 &lt;td&gt;Done&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;1&lt;/td&gt;
 &lt;td&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/frameworks-tools/blender-python/" &gt;Approach documented&lt;/a&gt; — Python scripted geometry, config-block iteration pattern&lt;/td&gt;
 &lt;td&gt;Done&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;2&lt;/td&gt;
 &lt;td&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; — first implementation; flat plate, boolean hole grid, engraved text, automated renders&lt;/td&gt;
 &lt;td&gt;Done&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;3&lt;/td&gt;
 &lt;td&gt;Headless render — Blender 5.1.2 + Dagger pipeline; STL → multi-angle PNGs without opening Blender; smoke-tested and running in CI&lt;/td&gt;
 &lt;td&gt;Done&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;4&lt;/td&gt;
 &lt;td&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/garage/scout-buckle/" &gt;Scout Buckle&lt;/a&gt; — complex geometry; compound curves, functional tongue mechanism, tolerances that matter&lt;/td&gt;
 &lt;td&gt;In progress&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;5&lt;/td&gt;
 &lt;td&gt;CI/CD pipeline — STL or script change → GitHub Actions → Dagger → PNG artifacts; &lt;a class="link" href="https://github.com/Backend-Engineering-Strategy-Tools/procedural-mesh-pipeline" target="_blank" rel="noopener"
 &gt;procedural-mesh-pipeline&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Done&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;6&lt;/td&gt;
 &lt;td&gt;AI feedback loop — describe correction → updated script → re-run, without opening Blender&lt;/td&gt;
 &lt;td&gt;Planned&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;7&lt;/td&gt;
 &lt;td&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/garage/dragon-split/" &gt;Dragon Split&lt;/a&gt; — mesh manipulation; cut an existing articulated dragon STL into printable segments with connectors&lt;/td&gt;
 &lt;td&gt;Not started&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="notes"&gt;Notes
&lt;/h2&gt;&lt;p&gt;The rack brace (step 2) was easier than expected — straightforward geometry, minimal iteration. That is why the pipeline steps did not get built yet: the manual workflow was fast enough that the overhead of automating it was not justified for one part.&lt;/p&gt;
&lt;p&gt;The scout buckle (step 4) is the harder test. Compound curves and functional constraints mean the script has needed partial rebuilds rather than config-block edits. That is the case that justified building the render pipeline properly.&lt;/p&gt;
&lt;p&gt;Steps 3 and 5 ended up built together rather than sequentially. The pipeline lives in a separate repo — &lt;a class="link" href="https://github.com/Backend-Engineering-Strategy-Tools/procedural-mesh-pipeline" target="_blank" rel="noopener"
 &gt;procedural-mesh-pipeline&lt;/a&gt; — Dagger + GitHub Actions, Blender 5.1.2 headless, Cycles CPU. Renders run natively on the CI runner (amd64); locally the pipeline is used for smoke-testing the image only. Blender has no official ARM64 Linux binary so local renders under emulation are impractical.&lt;/p&gt;
&lt;p&gt;The dragon split (step 7) is a different class of problem — remixing rather than generating. Worth keeping in the same project because the tooling overlaps (Blender Python, STL export) even if the approach does not.&lt;/p&gt;</description></item><item><title>Rack Support Brace — Programmatic 3D Print</title><link>https://backend-engineering-strategy-tools.github.io/site/homelab/rack-support-brace/</link><pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/homelab/rack-support-brace/</guid><description>&lt;p&gt;220×150×8mm support brace for the server rack. 40 through-holes in an alternating-spacing grid, B.E.S.T engraved into the top face, exported to STL — all generated by a Python script running in Blender. No manual modelling.&lt;/p&gt;




&lt;div class="sc-carousel" id="carousel-0"&gt;
 &lt;div class="sc-track"&gt;
 
 &lt;div class="sc-slide"&gt;
 &lt;img src="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/rack_support_brace/renders/rack_support_brace_220x150_front.png" loading="eager" alt="/code/procedural-mesh/rack_support_brace/renders/rack_support_brace_220x150_front.png"&gt;
 &lt;/div&gt;
 
 &lt;div class="sc-slide"&gt;
 &lt;img src="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/rack_support_brace/renders/rack_support_brace_220x150_iso.png" loading="lazy" alt="/code/procedural-mesh/rack_support_brace/renders/rack_support_brace_220x150_iso.png"&gt;
 &lt;/div&gt;
 
 &lt;div class="sc-slide"&gt;
 &lt;img src="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/rack_support_brace/renders/rack_support_brace_220x150_side.png" loading="lazy" alt="/code/procedural-mesh/rack_support_brace/renders/rack_support_brace_220x150_side.png"&gt;
 &lt;/div&gt;
 
 &lt;div class="sc-slide"&gt;
 &lt;img src="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/rack_support_brace/renders/rack_support_brace_220x150_top.png" loading="lazy" alt="/code/procedural-mesh/rack_support_brace/renders/rack_support_brace_220x150_top.png"&gt;
 &lt;/div&gt;
 
 &lt;/div&gt;
 
 &lt;button class="sc-btn sc-prev" aria-label="Previous"&gt;&amp;#8249;&lt;/button&gt;
 &lt;button class="sc-btn sc-next" aria-label="Next"&gt;&amp;#8250;&lt;/button&gt;
 &lt;div class="sc-dots"&gt;
 
 &lt;span class="sc-dot active"&gt;&lt;/span&gt;
 
 &lt;span class="sc-dot"&gt;&lt;/span&gt;
 
 &lt;span class="sc-dot"&gt;&lt;/span&gt;
 
 &lt;span class="sc-dot"&gt;&lt;/span&gt;
 
 &lt;/div&gt;
 
&lt;/div&gt;

&lt;style&gt;
.sc-carousel{position:relative;overflow:hidden;background:#0d0d0d;border-radius:8px;margin:1.5rem 0;user-select:none}
.sc-track{display:flex;transition:transform .4s ease}
.sc-slide{min-width:100%;display:flex;align-items:center;justify-content:center}
.sc-slide img{max-width:100%;max-height:560px;object-fit:contain}
.sc-btn{position:absolute;top:50%;transform:translateY(-50%);background:rgba(0,0,0,.55);color:#fff;border:none;font-size:2.4rem;line-height:1;padding:.05rem .65rem .15rem;cursor:pointer;border-radius:4px;z-index:10;transition:background .2s}
.sc-btn:hover{background:rgba(0,0,0,.85)}
.sc-prev{left:.5rem}.sc-next{right:.5rem}
.sc-dots{position:absolute;bottom:.65rem;left:0;right:0;display:flex;justify-content:center;gap:6px}
.sc-dot{width:8px;height:8px;background:rgba(255,255,255,.35);border-radius:50%;cursor:pointer;transition:background .2s}
.sc-dot.active{background:#fff}
&lt;/style&gt;

&lt;script&gt;
(function(){
 var root=document.getElementById('carousel-0');
 if(!root)return;
 var track=root.querySelector('.sc-track');
 var slides=root.querySelectorAll('.sc-slide');
 var dots=root.querySelectorAll('.sc-dot');
 var cur=0,timer;
 function go(n){
 if(dots[cur])dots[cur].classList.remove('active');
 cur=((n%slides.length)+slides.length)%slides.length;
 if(dots[cur])dots[cur].classList.add('active');
 track.style.transform='translateX(-'+cur*100+'%)';
 }
 var prev=root.querySelector('.sc-prev');
 var next=root.querySelector('.sc-next');
 if(prev)prev.addEventListener('click',function(){clearInterval(timer);go(cur-1);start()});
 if(next)next.addEventListener('click',function(){clearInterval(timer);go(cur+1);start()});
 dots.forEach(function(d,i){d.addEventListener('click',function(){clearInterval(timer);go(i);start()})});
 function start(){timer=setInterval(function(){go(cur+1)},"2800");}
 start();
})();
&lt;/script&gt;

&lt;p&gt;The same script that builds the geometry now also sets up cameras, runs EEVEE renders from four angles, and saves PNGs alongside the STL. The renders above are the direct output.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="downloads"&gt;Downloads
&lt;/h2&gt;&lt;p&gt;


&lt;a href="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/rack_support_brace/rack_support_brace.py" download class="dl-link"&gt;
 &lt;span class="dl-icon"&gt;↓&lt;/span&gt;rack_support_brace.py
&lt;/a&gt;

&lt;style&gt;
.dl-link{display:inline-flex;align-items:center;gap:.4rem;padding:.3rem .75rem;border:1px solid currentColor;border-radius:4px;font-family:monospace;font-size:.875rem;text-decoration:none;opacity:.85;transition:opacity .15s}
.dl-link:hover{opacity:1;text-decoration:none}
.dl-icon{font-size:1rem;line-height:1}
&lt;/style&gt;




&lt;a href="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/rack_support_brace/rack_support_brace_220x150.stl" download class="dl-link"&gt;
 &lt;span class="dl-icon"&gt;↓&lt;/span&gt;rack_support_brace_220x150.stl
&lt;/a&gt;

&lt;style&gt;
.dl-link{display:inline-flex;align-items:center;gap:.4rem;padding:.3rem .75rem;border:1px solid currentColor;border-radius:4px;font-family:monospace;font-size:.875rem;text-decoration:none;opacity:.85;transition:opacity .15s}
.dl-link:hover{opacity:1;text-decoration:none}
.dl-icon{font-size:1rem;line-height:1}
&lt;/style&gt;
&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-script"&gt;The script
&lt;/h2&gt;&lt;p&gt;Flip &lt;code&gt;EXPORT_STL&lt;/code&gt; / &lt;code&gt;EXPORT_RENDER&lt;/code&gt; at the top of the file, point &lt;code&gt;EXPORT_DIR&lt;/code&gt; at your folder, press &lt;code&gt;Alt+P&lt;/code&gt; in the Blender Script Editor.&lt;/p&gt;
&lt;p&gt;The config block at the top of the file is the only place dimensions live:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;PLATE_X = 220.0 # mm
PLATE_Y = 150.0 # mm
HOLE_DIAM = 7.0 # mm
N_COLS = 10
ROW_SPACING = 20.0 # mm
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Iterating means editing a constant and re-running — not touching a model.&lt;/p&gt;
&lt;p&gt;See &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/frameworks-tools/blender-python/" &gt;Blender Python for 3D Printing&lt;/a&gt; for the general pattern behind this.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-1--render-from-script-"&gt;Step 1 — Render from script ✓
&lt;/h2&gt;&lt;p&gt;Done. Script builds geometry → exports STL → places cameras at four angles (top, front, side, iso) → EEVEE renders → PNGs saved to &lt;code&gt;EXPORT_DIR/renders/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Pipeline next steps (headless render, CI/CD, AI feedback loop) are tracked in the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/projects/procedural-mesh/" &gt;Procedural Mesh project&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-printed-part"&gt;The Printed Part
&lt;/h2&gt;&lt;figure&gt;&lt;img src="https://backend-engineering-strategy-tools.github.io/site/images/rack-brace.jpeg"
 alt="Finished rack support brace installed in the rack."&gt;&lt;figcaption&gt;
 &lt;p&gt;Finished rack support brace installed in the rack.&lt;/p&gt;
 &lt;/figcaption&gt;
&lt;/figure&gt;
</description></item><item><title>Resin Badges — Packat &amp; Klart</title><link>https://backend-engineering-strategy-tools.github.io/site/garage/scout-badges-resin/</link><pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/garage/scout-badges-resin/</guid><description>&lt;p&gt;Resin-printed &amp;lsquo;packat &amp;amp; klart&amp;rsquo; badges for the scouts heading out on their first overnight trip, i.e. &amp;lsquo;Hajk&amp;rsquo;.&lt;/p&gt;
&lt;p&gt;We had a meeting where we went through what one should bring, then ran a competition where the kids got points for guessing what was in the pack I brought along. They got a badge if they did well — they all passed. Success.&lt;/p&gt;
&lt;p&gt;First attempt at producing physical scout items rather than sourcing them.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-badge"&gt;The Badge
&lt;/h2&gt;&lt;p&gt;Modeled in Blender, exported to STL, printed in resin.&lt;/p&gt;




&lt;div class="sc-carousel" id="carousel-0"&gt;
 &lt;div class="sc-track"&gt;
 
 &lt;div class="sc-slide"&gt;
 &lt;img src="https://backend-engineering-strategy-tools.github.io/site/code/mesh/packat_klart/renders/model_back.png" loading="eager" alt="/code/mesh/packat_klart/renders/model_back.png"&gt;
 &lt;/div&gt;
 
 &lt;div class="sc-slide"&gt;
 &lt;img src="https://backend-engineering-strategy-tools.github.io/site/code/mesh/packat_klart/renders/model_front.png" loading="lazy" alt="/code/mesh/packat_klart/renders/model_front.png"&gt;
 &lt;/div&gt;
 
 &lt;div class="sc-slide"&gt;
 &lt;img src="https://backend-engineering-strategy-tools.github.io/site/code/mesh/packat_klart/renders/model_iso.png" loading="lazy" alt="/code/mesh/packat_klart/renders/model_iso.png"&gt;
 &lt;/div&gt;
 
 &lt;div class="sc-slide"&gt;
 &lt;img src="https://backend-engineering-strategy-tools.github.io/site/code/mesh/packat_klart/renders/model_side.png" loading="lazy" alt="/code/mesh/packat_klart/renders/model_side.png"&gt;
 &lt;/div&gt;
 
 &lt;div class="sc-slide"&gt;
 &lt;img src="https://backend-engineering-strategy-tools.github.io/site/code/mesh/packat_klart/renders/model_top.png" loading="lazy" alt="/code/mesh/packat_klart/renders/model_top.png"&gt;
 &lt;/div&gt;
 
 &lt;/div&gt;
 
 &lt;button class="sc-btn sc-prev" aria-label="Previous"&gt;&amp;#8249;&lt;/button&gt;
 &lt;button class="sc-btn sc-next" aria-label="Next"&gt;&amp;#8250;&lt;/button&gt;
 &lt;div class="sc-dots"&gt;
 
 &lt;span class="sc-dot active"&gt;&lt;/span&gt;
 
 &lt;span class="sc-dot"&gt;&lt;/span&gt;
 
 &lt;span class="sc-dot"&gt;&lt;/span&gt;
 
 &lt;span class="sc-dot"&gt;&lt;/span&gt;
 
 &lt;span class="sc-dot"&gt;&lt;/span&gt;
 
 &lt;/div&gt;
 
&lt;/div&gt;

&lt;style&gt;
.sc-carousel{position:relative;overflow:hidden;background:#0d0d0d;border-radius:8px;margin:1.5rem 0;user-select:none}
.sc-track{display:flex;transition:transform .4s ease}
.sc-slide{min-width:100%;display:flex;align-items:center;justify-content:center}
.sc-slide img{max-width:100%;max-height:560px;object-fit:contain}
.sc-btn{position:absolute;top:50%;transform:translateY(-50%);background:rgba(0,0,0,.55);color:#fff;border:none;font-size:2.4rem;line-height:1;padding:.05rem .65rem .15rem;cursor:pointer;border-radius:4px;z-index:10;transition:background .2s}
.sc-btn:hover{background:rgba(0,0,0,.85)}
.sc-prev{left:.5rem}.sc-next{right:.5rem}
.sc-dots{position:absolute;bottom:.65rem;left:0;right:0;display:flex;justify-content:center;gap:6px}
.sc-dot{width:8px;height:8px;background:rgba(255,255,255,.35);border-radius:50%;cursor:pointer;transition:background .2s}
.sc-dot.active{background:#fff}
&lt;/style&gt;

&lt;script&gt;
(function(){
 var root=document.getElementById('carousel-0');
 if(!root)return;
 var track=root.querySelector('.sc-track');
 var slides=root.querySelectorAll('.sc-slide');
 var dots=root.querySelectorAll('.sc-dot');
 var cur=0,timer;
 function go(n){
 if(dots[cur])dots[cur].classList.remove('active');
 cur=((n%slides.length)+slides.length)%slides.length;
 if(dots[cur])dots[cur].classList.add('active');
 track.style.transform='translateX(-'+cur*100+'%)';
 }
 var prev=root.querySelector('.sc-prev');
 var next=root.querySelector('.sc-next');
 if(prev)prev.addEventListener('click',function(){clearInterval(timer);go(cur-1);start()});
 if(next)next.addEventListener('click',function(){clearInterval(timer);go(cur+1);start()});
 dots.forEach(function(d,i){d.addEventListener('click',function(){clearInterval(timer);go(i);start()})});
 function start(){timer=setInterval(function(){go(cur+1)},"2800");}
 start();
})();
&lt;/script&gt;

&lt;p&gt;


&lt;a href="https://backend-engineering-strategy-tools.github.io/site/code/mesh/packat_klart/packat_klart.blend" download class="dl-link"&gt;
 &lt;span class="dl-icon"&gt;↓&lt;/span&gt;packat_klart.blend
&lt;/a&gt;

&lt;style&gt;
.dl-link{display:inline-flex;align-items:center;gap:.4rem;padding:.3rem .75rem;border:1px solid currentColor;border-radius:4px;font-family:monospace;font-size:.875rem;text-decoration:none;opacity:.85;transition:opacity .15s}
.dl-link:hover{opacity:1;text-decoration:none}
.dl-icon{font-size:1rem;line-height:1}
&lt;/style&gt;




&lt;a href="https://backend-engineering-strategy-tools.github.io/site/code/mesh/packat_klart/packat_och_klart.stl" download class="dl-link"&gt;
 &lt;span class="dl-icon"&gt;↓&lt;/span&gt;packat_och_klart.stl
&lt;/a&gt;

&lt;style&gt;
.dl-link{display:inline-flex;align-items:center;gap:.4rem;padding:.3rem .75rem;border:1px solid currentColor;border-radius:4px;font-family:monospace;font-size:.875rem;text-decoration:none;opacity:.85;transition:opacity .15s}
.dl-link:hover{opacity:1;text-decoration:none}
.dl-icon{font-size:1rem;line-height:1}
&lt;/style&gt;
&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="print"&gt;Print
&lt;/h2&gt;&lt;p&gt;Printed on the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/garage/3d-printing/" &gt;Anycubic Photon Mono 2&lt;/a&gt; in Craftsman resin for the extra detail. In hindsight standard resin would have been fine at this size.&lt;/p&gt;
&lt;hr&gt;</description></item><item><title>Scout Buckle Clone — Parametric for Casting</title><link>https://backend-engineering-strategy-tools.github.io/site/garage/scout-buckle/</link><pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/garage/scout-buckle/</guid><description>&lt;p&gt;Clone of a Scout buckle — built in Blender Python for casting rather than FDM printing. The goal is a dimensionally accurate reproduction suitable for resin or lost-wax casting.&lt;/p&gt;
&lt;p&gt;What makes this different from the rack brace: the buckle geometry is not a simple boolean grid. It has compound curves, a tongue mechanism, and tolerances that matter for function. One-shotting it in Python is harder — the script has gone through multiple partial rebuilds rather than a single config-block iteration.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="scripts"&gt;Scripts
&lt;/h2&gt;&lt;p&gt;


&lt;a href="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/buckle/buckle_v1.py" download class="dl-link"&gt;
 &lt;span class="dl-icon"&gt;↓&lt;/span&gt;buckle_v1.py
&lt;/a&gt;

&lt;style&gt;
.dl-link{display:inline-flex;align-items:center;gap:.4rem;padding:.3rem .75rem;border:1px solid currentColor;border-radius:4px;font-family:monospace;font-size:.875rem;text-decoration:none;opacity:.85;transition:opacity .15s}
.dl-link:hover{opacity:1;text-decoration:none}
.dl-icon{font-size:1rem;line-height:1}
&lt;/style&gt;




&lt;a href="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/buckle/buckle_part1.py" download class="dl-link"&gt;
 &lt;span class="dl-icon"&gt;↓&lt;/span&gt;buckle_part1.py
&lt;/a&gt;

&lt;style&gt;
.dl-link{display:inline-flex;align-items:center;gap:.4rem;padding:.3rem .75rem;border:1px solid currentColor;border-radius:4px;font-family:monospace;font-size:.875rem;text-decoration:none;opacity:.85;transition:opacity .15s}
.dl-link:hover{opacity:1;text-decoration:none}
.dl-icon{font-size:1rem;line-height:1}
&lt;/style&gt;




&lt;a href="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/buckle/buckle_part2_v1.py" download class="dl-link"&gt;
 &lt;span class="dl-icon"&gt;↓&lt;/span&gt;buckle_part2_v1.py
&lt;/a&gt;

&lt;style&gt;
.dl-link{display:inline-flex;align-items:center;gap:.4rem;padding:.3rem .75rem;border:1px solid currentColor;border-radius:4px;font-family:monospace;font-size:.875rem;text-decoration:none;opacity:.85;transition:opacity .15s}
.dl-link:hover{opacity:1;text-decoration:none}
.dl-icon{font-size:1rem;line-height:1}
&lt;/style&gt;




&lt;a href="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/buckle/buckle_part2_v2.py" download class="dl-link"&gt;
 &lt;span class="dl-icon"&gt;↓&lt;/span&gt;buckle_part2_v2.py
&lt;/a&gt;

&lt;style&gt;
.dl-link{display:inline-flex;align-items:center;gap:.4rem;padding:.3rem .75rem;border:1px solid currentColor;border-radius:4px;font-family:monospace;font-size:.875rem;text-decoration:none;opacity:.85;transition:opacity .15s}
.dl-link:hover{opacity:1;text-decoration:none}
.dl-icon{font-size:1rem;line-height:1}
&lt;/style&gt;
&lt;/p&gt;
&lt;p&gt;The split into &lt;code&gt;part1&lt;/code&gt; / &lt;code&gt;part2&lt;/code&gt; reflects the geometry breakdown — building the frame and the tongue separately before joining, which turned out to be easier to iterate on than a single monolithic script.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="what-the-iteration-looks-like"&gt;What the iteration looks like
&lt;/h2&gt;&lt;p&gt;&lt;em&gt;Notes to follow.&lt;/em&gt; The short version: parametric geometry that has functional constraints (a tongue that must seat and release under load) fights back more than decorative geometry. Changing one dimension propagates into several others in ways that aren&amp;rsquo;t obvious from a config block alone.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="casting-considerations"&gt;Casting considerations
&lt;/h2&gt;&lt;p&gt;&lt;em&gt;Notes to follow.&lt;/em&gt; Draft angles, wall thickness minimums, and sprue placement are constraints that don&amp;rsquo;t exist in FDM — the script needs to know about them.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="status"&gt;Status
&lt;/h2&gt;&lt;p&gt;Work in progress. Geometry is close; casting prep not started.&lt;/p&gt;
&lt;p&gt;See also: &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/rack-support-brace/" &gt;Rack Support Brace&lt;/a&gt; — simpler parametric baseline using the same approach.&lt;/p&gt;</description></item><item><title>3D Printed Rack Parts</title><link>https://backend-engineering-strategy-tools.github.io/site/homelab/rack-3d-prints/</link><pubDate>Sun, 12 Apr 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/homelab/rack-3d-prints/</guid><description>&lt;p&gt;Filling gaps in the rack build with printed parts — ears, blanks, and a modular tray system. Mix of sourced models from Printables and geometry scripted in Blender Python.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="sourced-models"&gt;Sourced Models
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Model&lt;/th&gt;
 &lt;th&gt;What it is&lt;/th&gt;
 &lt;th&gt;Status&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://www.printables.com/model/1335735-1u-universal-rack-ears" target="_blank" rel="noopener"
 &gt;1U Universal Rack Ears&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Rack ears for gear that ships without them&lt;/td&gt;
 &lt;td&gt;Printed&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://www.printables.com/model/1374690-standard-rack-1u-2u-3u-4u-spacer-dxf-template" target="_blank" rel="noopener"
 &gt;1U–4U Spacer / Blank Panel&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Blank panels to fill empty rack units&lt;/td&gt;
 &lt;td&gt;Printed&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://makerworld.com/en/models/1040867-rackmod-1u-slide-a-modular-server-rack-system" target="_blank" rel="noopener"
 &gt;RackMod 1U Slide-A&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Modular 1U tray system with slide-in modules&lt;/td&gt;
 &lt;td&gt;Testing&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://www.printables.com/model/1184008-19-1u-server-rack-cover-plates-update-2u-3u" target="_blank" rel="noopener"
 &gt;19&amp;quot; 1U/2U/3U Cover Plates&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Solid cover/blanking plates for 19&amp;quot; rack&lt;/td&gt;
 &lt;td&gt;Queued&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://www.printables.com/model/1350350-1u-rack-cable-ears" target="_blank" rel="noopener"
 &gt;1U Rack Cable Ears&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Rack ears with integrated cable management&lt;/td&gt;
 &lt;td&gt;Queued&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Raspberry Pi 1U rack mount&lt;/td&gt;
 &lt;td&gt;1U mount for RPi in 19&amp;quot; rack (BIFROST)&lt;/td&gt;
 &lt;td&gt;Printed&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://www.printables.com/model/1300803-zip-tie-clip-45-mm-t-slot-extrusion" target="_blank" rel="noopener"
 &gt;Zip Tie Clip 45mm T-Slot&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Cable management clips for 45mm extrusion&lt;/td&gt;
 &lt;td&gt;Queued&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="scripted-parts"&gt;Scripted Parts
&lt;/h2&gt;&lt;p&gt;Geometry generated by a Python script running inside Blender rather than modeled by hand. The immediate need was a support brace for the rack — a 220×150×8mm plate with 40 through-holes in a specific alternating-spacing pattern, the B.E.S.T label engraved into the top face, exported directly to STL.&lt;/p&gt;
&lt;p&gt;Writing it in Python means the layout lives in a config block at the top of the file. Changing hole count, plate dimensions, or row spacing is a constant, not a modeling operation.&lt;/p&gt;
&lt;ul&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; — 220×150mm plate, 10×4 hole grid, engraved text, automated renders | &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/rack_support_brace/rack_support_brace.py" &gt;script&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More on the approach: &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/frameworks-tools/blender-python/" &gt;Blender Python for 3D printing&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Print setup&lt;/strong&gt;: &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/garage/3d-printing/" &gt;3D Printing — Garage&lt;/a&gt;&lt;/p&gt;</description></item><item><title>3D Printing</title><link>https://backend-engineering-strategy-tools.github.io/site/garage/3d-printing/</link><pubDate>Sun, 12 Apr 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/garage/3d-printing/</guid><description>&lt;p&gt;Two printers: an FDM machine for structural and functional parts, a resin printer for detail work. Different tools for different jobs — the resin produces sharper geometry at the cost of more process overhead.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="fdm--anycubic-kobra-x"&gt;FDM — Anycubic Kobra X
&lt;/h2&gt;&lt;p&gt;Current machine. Workhorse for rack accessories, enclosures, and anything that needs to be durable and dimensionally accurate. Printing in PLA for most jobs.&lt;/p&gt;
&lt;p&gt;Replaced an older Prusa i3 MK0 that still works but is no longer the daily driver. Shelved for now. CNC conversion or rebuild is somewhere on the list, parts donor if it comes to that first.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Build volume&lt;/td&gt;
 &lt;td&gt;260 × 260 × 260 mm&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Bed&lt;/td&gt;
 &lt;td&gt;PEI spring steel, max 100°C&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Nozzle (stock)&lt;/td&gt;
 &lt;td&gt;0.4 mm hardened steel, max 300°C&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Speed&lt;/td&gt;
 &lt;td&gt;300 mm/s recommended, 600 mm/s max&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Extrusion&lt;/td&gt;
 &lt;td&gt;Direct drive&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Leveling&lt;/td&gt;
 &lt;td&gt;LeviQ3.0 auto-leveling&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Multicolor&lt;/td&gt;
 &lt;td&gt;4-colour native (ACE 2 Pro), expandable to 19&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Extras&lt;/td&gt;
 &lt;td&gt;AI spaghetti detection, HD camera, filament runout&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Nozzles on hand&lt;/strong&gt;: 0.4 mm (stock), 0.25 mm (not tried yet). Expandable to 0.6 / 0.8 mm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Filament on hand&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Brand&lt;/th&gt;
 &lt;th&gt;Material&lt;/th&gt;
 &lt;th&gt;Colour&lt;/th&gt;
 &lt;th&gt;Qty&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Verbatim&lt;/td&gt;
 &lt;td&gt;PLA&lt;/td&gt;
 &lt;td&gt;Black&lt;/td&gt;
 &lt;td&gt;1 kg&lt;/td&gt;
 &lt;td&gt;Original stock&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Bambu Lab&lt;/td&gt;
 &lt;td&gt;PLA Basic&lt;/td&gt;
 &lt;td&gt;Orange&lt;/td&gt;
 &lt;td&gt;1 kg&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Bambu Lab&lt;/td&gt;
 &lt;td&gt;PLA Basic&lt;/td&gt;
 &lt;td&gt;Green&lt;/td&gt;
 &lt;td&gt;1 kg&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Bambu Lab&lt;/td&gt;
 &lt;td&gt;PLA Basic&lt;/td&gt;
 &lt;td&gt;Magenta&lt;/td&gt;
 &lt;td&gt;1 kg&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Bambu Lab&lt;/td&gt;
 &lt;td&gt;PLA Basic&lt;/td&gt;
 &lt;td&gt;Clear&lt;/td&gt;
 &lt;td&gt;1 kg&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Bambu Lab&lt;/td&gt;
 &lt;td&gt;PLA Basic&lt;/td&gt;
 &lt;td&gt;Red&lt;/td&gt;
 &lt;td&gt;1 kg&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Bambu Lab&lt;/td&gt;
 &lt;td&gt;PETG&lt;/td&gt;
 &lt;td&gt;Red&lt;/td&gt;
 &lt;td&gt;1 kg&lt;/td&gt;
 &lt;td&gt;Refill spool&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Bambu Lab&lt;/td&gt;
 &lt;td&gt;PETG&lt;/td&gt;
 &lt;td&gt;Blue&lt;/td&gt;
 &lt;td&gt;1 kg&lt;/td&gt;
 &lt;td&gt;Refill spool&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Bambu Lab&lt;/td&gt;
 &lt;td&gt;PETG&lt;/td&gt;
 &lt;td&gt;Black&lt;/td&gt;
 &lt;td&gt;1 kg&lt;/td&gt;
 &lt;td&gt;Refill spool&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SUNLU&lt;/td&gt;
 &lt;td&gt;PLA&lt;/td&gt;
 &lt;td&gt;Black&lt;/td&gt;
 &lt;td&gt;0.25 kg&lt;/td&gt;
 &lt;td&gt;Sampler pack&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SUNLU&lt;/td&gt;
 &lt;td&gt;PLA&lt;/td&gt;
 &lt;td&gt;White&lt;/td&gt;
 &lt;td&gt;0.25 kg&lt;/td&gt;
 &lt;td&gt;Sampler pack&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SUNLU&lt;/td&gt;
 &lt;td&gt;PLA&lt;/td&gt;
 &lt;td&gt;Grey&lt;/td&gt;
 &lt;td&gt;0.25 kg&lt;/td&gt;
 &lt;td&gt;Sampler pack&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SUNLU&lt;/td&gt;
 &lt;td&gt;PLA&lt;/td&gt;
 &lt;td&gt;Red&lt;/td&gt;
 &lt;td&gt;0.25 kg&lt;/td&gt;
 &lt;td&gt;Sampler pack&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SUNLU&lt;/td&gt;
 &lt;td&gt;PLA&lt;/td&gt;
 &lt;td&gt;Light Blue&lt;/td&gt;
 &lt;td&gt;0.25 kg&lt;/td&gt;
 &lt;td&gt;Sampler pack&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SUNLU&lt;/td&gt;
 &lt;td&gt;PLA&lt;/td&gt;
 &lt;td&gt;Light Yellow&lt;/td&gt;
 &lt;td&gt;0.25 kg&lt;/td&gt;
 &lt;td&gt;Sampler pack&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SUNLU&lt;/td&gt;
 &lt;td&gt;PLA&lt;/td&gt;
 &lt;td&gt;Green&lt;/td&gt;
 &lt;td&gt;0.25 kg&lt;/td&gt;
 &lt;td&gt;Sampler pack&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SUNLU&lt;/td&gt;
 &lt;td&gt;PLA&lt;/td&gt;
 &lt;td&gt;Orange&lt;/td&gt;
 &lt;td&gt;0.25 kg&lt;/td&gt;
 &lt;td&gt;Sampler pack&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SUNLU&lt;/td&gt;
 &lt;td&gt;PLA&lt;/td&gt;
 &lt;td&gt;Pink&lt;/td&gt;
 &lt;td&gt;0.25 kg&lt;/td&gt;
 &lt;td&gt;Sampler pack&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SUNLU&lt;/td&gt;
 &lt;td&gt;PLA&lt;/td&gt;
 &lt;td&gt;Lavender Purple&lt;/td&gt;
 &lt;td&gt;0.25 kg&lt;/td&gt;
 &lt;td&gt;Sampler pack&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SUNLU&lt;/td&gt;
 &lt;td&gt;PLA&lt;/td&gt;
 &lt;td&gt;Brown&lt;/td&gt;
 &lt;td&gt;0.25 kg&lt;/td&gt;
 &lt;td&gt;Sampler pack&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SUNLU&lt;/td&gt;
 &lt;td&gt;PLA&lt;/td&gt;
 &lt;td&gt;Olive Green&lt;/td&gt;
 &lt;td&gt;0.25 kg&lt;/td&gt;
 &lt;td&gt;Sampler pack&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SUNLU&lt;/td&gt;
 &lt;td&gt;PLA&lt;/td&gt;
 &lt;td&gt;Oak&lt;/td&gt;
 &lt;td&gt;0.25 kg&lt;/td&gt;
 &lt;td&gt;Sampler pack&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SUNLU&lt;/td&gt;
 &lt;td&gt;PLA&lt;/td&gt;
 &lt;td&gt;Skin&lt;/td&gt;
 &lt;td&gt;0.25 kg&lt;/td&gt;
 &lt;td&gt;Sampler pack&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SUNLU&lt;/td&gt;
 &lt;td&gt;PLA&lt;/td&gt;
 &lt;td&gt;Transparent&lt;/td&gt;
 &lt;td&gt;0.25 kg&lt;/td&gt;
 &lt;td&gt;Sampler pack&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;TECBEARS&lt;/td&gt;
 &lt;td&gt;PLA Matte&lt;/td&gt;
 &lt;td&gt;Black&lt;/td&gt;
 &lt;td&gt;10 kg&lt;/td&gt;
 &lt;td&gt;High-speed rated (600 mm/s), bulk stock&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="resin--anycubic-photon-mono-2"&gt;Resin — Anycubic Photon Mono 2
&lt;/h2&gt;&lt;p&gt;Mono LCD resin printer. Used for detail parts — scout badges, finer geometry — where FDM resolution isn&amp;rsquo;t enough. Paired with an Anycubic Wash &amp;amp; Cure 3 for post-processing.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Build volume&lt;/td&gt;
 &lt;td&gt;143 × 89 × 165 mm&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Screen&lt;/td&gt;
 &lt;td&gt;6.6&amp;quot; Mono LCD, 4096 × 2560, ~2000 hrs&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;XY resolution&lt;/td&gt;
 &lt;td&gt;34 μm&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Z accuracy&lt;/td&gt;
 &lt;td&gt;10 μm (single linear rail)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Print speed&lt;/td&gt;
 &lt;td&gt;≤ 50 mm/hr&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Leveling&lt;/td&gt;
 &lt;td&gt;4-point manual&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Light source&lt;/td&gt;
 &lt;td&gt;Parallel matrix&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Build platform&lt;/td&gt;
 &lt;td&gt;Laser-engraved aluminium alloy&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Data input&lt;/td&gt;
 &lt;td&gt;USB Type-A 2.0&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Wash &amp;amp; Cure 3&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Wash capacity&lt;/td&gt;
 &lt;td&gt;Fits Mono 2 build plate&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;UV wavelength&lt;/td&gt;
 &lt;td&gt;405 nm&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Cure time&lt;/td&gt;
 &lt;td&gt;~2–3 min&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Resin on hand&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Resin&lt;/th&gt;
 &lt;th&gt;Type&lt;/th&gt;
 &lt;th&gt;Colour&lt;/th&gt;
 &lt;th&gt;Qty&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;ABS-Like V2&lt;/td&gt;
 &lt;td&gt;ABS-Like&lt;/td&gt;
 &lt;td&gt;Black&lt;/td&gt;
 &lt;td&gt;~3 kg&lt;/td&gt;
 &lt;td&gt;Structural / strength&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ABS-Like 2.0&lt;/td&gt;
 &lt;td&gt;ABS-Like&lt;/td&gt;
 &lt;td&gt;Beige&lt;/td&gt;
 &lt;td&gt;8 kg&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ABS-Like 2.0&lt;/td&gt;
 &lt;td&gt;ABS-Like&lt;/td&gt;
 &lt;td&gt;Translucent Green&lt;/td&gt;
 &lt;td&gt;1 kg&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ABS-Like 2.0&lt;/td&gt;
 &lt;td&gt;ABS-Like&lt;/td&gt;
 &lt;td&gt;Clear&lt;/td&gt;
 &lt;td&gt;1 kg&lt;/td&gt;
 &lt;td&gt;For lens work eventually&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Standard V2&lt;/td&gt;
 &lt;td&gt;Standard&lt;/td&gt;
 &lt;td&gt;Black&lt;/td&gt;
 &lt;td&gt;~0.5 kg&lt;/td&gt;
 &lt;td&gt;Display / detail&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Standard&lt;/td&gt;
 &lt;td&gt;Standard&lt;/td&gt;
 &lt;td&gt;Light Beige&lt;/td&gt;
 &lt;td&gt;8 kg&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Standard&lt;/td&gt;
 &lt;td&gt;Standard&lt;/td&gt;
 &lt;td&gt;Translucent Green&lt;/td&gt;
 &lt;td&gt;1 kg&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Standard&lt;/td&gt;
 &lt;td&gt;Standard&lt;/td&gt;
 &lt;td&gt;Clear&lt;/td&gt;
 &lt;td&gt;1 kg&lt;/td&gt;
 &lt;td&gt;For lens work eventually&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Craftsman&lt;/td&gt;
 &lt;td&gt;Detail&lt;/td&gt;
 &lt;td&gt;Grey&lt;/td&gt;
 &lt;td&gt;~1 kg&lt;/td&gt;
 &lt;td&gt;Sharp detail, brittle&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;Detailed resin mixing notes and maintenance log kept separately.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="slicer--workflow"&gt;Slicer / Workflow
&lt;/h2&gt;&lt;p&gt;&lt;em&gt;Notes to follow — slicer setup, print profiles, export workflow.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Rack and homelab prints&lt;/strong&gt;: &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/rack-3d-prints/" &gt;3D Printed Rack Parts&lt;/a&gt;&lt;/p&gt;</description></item></channel></rss>