<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Lars | Algebraic</title>
    <link>https://algebraic.games/authors/lars/</link>
      <atom:link href="https://algebraic.games/authors/lars/index.xml" rel="self" type="application/rss+xml" />
    <description>Lars</description>
    <generator>HugoBlox Kit (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Sun, 15 Mar 2026 00:00:00 +0000</lastBuildDate>
    <image>
      <url>https://algebraic.games/media/authors/lars_hu_445165aae59fb4b6.jpg</url>
      <title>Lars</title>
      <link>https://algebraic.games/authors/lars/</link>
    </image>
    
    <item>
      <title>Desert Space Ship Scene</title>
      <link>https://algebraic.games/blog/desert_space_ship/</link>
      <pubDate>Sun, 15 Mar 2026 00:00:00 +0000</pubDate>
      <guid>https://algebraic.games/blog/desert_space_ship/</guid>
      <description>&lt;p&gt;&lt;strong&gt;Squishy Volumes&lt;/strong&gt; is a Blender add-on developed by Algebraic that puts the Material Point Method (MPM) directly into Blender.
Get the &lt;a href=&#34;https://github.com/Algebraic-UG/squishy_volumes/releases/latest&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;latest release&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;This blog post is about the making of a scene that shows a space ship emerging from the desert.
The purpose is to test and showcase Squishy Volumes&amp;rsquo; capabilities in scenes with &lt;strong&gt;millions of particles&lt;/strong&gt;.&lt;/p&gt;
&lt;iframe
  align=&#34;middle&#34;
  width=&#34;100%&#34;
  height=&#34;420&#34;
  allowfullscreen
  style=&#34;display:block;margin:auto;&#34;
  src=&#34;https://www.youtube.com/embed/BH0PTvSxFx8&#34;&gt;
&lt;/iframe&gt;
&lt;center&gt;&lt;figure&gt;
&lt;table&gt;
&lt;tr&gt;&lt;td&gt;Blender version:&lt;/td&gt;&lt;td&gt;5.0.1&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Squishy Volumes version:&lt;/td&gt;&lt;td&gt;0.2.0-alpha4&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Number of particles:&lt;/td&gt;&lt;td&gt;6.070.868&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Number of grid nodes (varies over time):&lt;/td&gt;&lt;td&gt;~1.500.000&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Simulated frames:&lt;/td&gt;&lt;td&gt;250&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Directory size:&lt;/td&gt;&lt;td&gt;786 GB 💀&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Bake time:&lt;/td&gt;&lt;td&gt;18.75 hrs&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Render time:&lt;/td&gt;&lt;td&gt;14.29 hrs&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mode:&lt;/td&gt;&lt;td&gt;Explicit, CPU &lt;a href=&#34;#footnote-1&#34;&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;figcaption&gt;Data for the nerds.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;p&gt;We’re going to look at this example of what we can do with Squishy Volumes and what challenges to look out for.
This blog post is not a step-by-step tutorial, though.&lt;/p&gt;
&lt;p&gt;The general idea of the scene is to showcase the &lt;em&gt;awesome power&lt;/em&gt; of this space ship.
Sand is &lt;em&gt;really heavy&lt;/em&gt;, so if the space ship is able to emerge from the depths of the desert and lift tons of material, yeah, that makes it all the more menacing.
Here&amp;rsquo;s the link to this super cool &lt;a href=&#34;https://tarasov3d-studio.itch.io/sci-fi-ship-lowpoly-3d-model&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Sci-Fi Ship LowPoly 3D model&lt;/a&gt;. &lt;a href=&#34;#footnote-2&#34;&gt;[2]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll need lots and lots of sand particles, some static flooring, and an animated collider for the simulation input.
For the output, we can make some slight modifications to the Squishy Volumes default visualization and of course, the background, etc.&lt;/p&gt;
&lt;p&gt;We’ll face some challenges in defining simulation input, baking, and simulation output.
Then we’ll discuss visualization tricks and conclude the blog post with a few comments on how the add-on can be improved.&lt;/p&gt;
&lt;p&gt;So, what are the challenges?&lt;/p&gt;
&lt;h1 id=&#34;challenges&#34;&gt;Challenges&lt;/h1&gt;
&lt;p&gt;Some parts are relatively easy; we won&amp;rsquo;t go into too much detail there.
For example, setting up the sand.&lt;/p&gt;
&lt;p&gt;Other parts are harder.
We’ll need to deal with non-manifold edges, inconsistent collider grids, exploding sand, and, last but not least, efficiently working with the outputs.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start with the input.&lt;/p&gt;
&lt;h2 id=&#34;simulation-input&#34;&gt;Simulation Input&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;ll have a particle input for the sand and three collider inputs: one for the floor and two for the space ship.
The model&amp;rsquo;s guns are provided as a separate object, and it&amp;rsquo;s more convenient to use them as a second animated collider.&lt;/p&gt;
&lt;center&gt;&lt;figure&gt;
&lt;img src=&#34;https://algebraic.games/blog/desert_space_ship/inputs.png&#34;/&gt;
&lt;figcaption&gt;The Squishy Volumes input panel listing our three inputs.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;h3 id=&#34;sand&#34;&gt;Sand&lt;/h3&gt;
&lt;p&gt;The sand was relatively easy to get &amp;ldquo;right&amp;rdquo;.
The sand is spawned in this simple square &amp;ldquo;bowl&amp;rdquo; with the Squishy Volumes default generating nodes.&lt;/p&gt;
&lt;center&gt;&lt;figure&gt;
&lt;img src=&#34;https://algebraic.games/blog/desert_space_ship/sand-shape.png&#34;/&gt;
&lt;figcaption&gt;Simple input shape, we can conceal it later.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;p&gt;It&amp;rsquo;s worth having the sand &amp;ldquo;soft&amp;rdquo;, since we pay for stiff material with a higher simulation runtime.
Here, it&amp;rsquo;s 1 MPa &lt;code&gt;Young&#39;s Modulus&lt;/code&gt;.
Realistic values are at least 10 times that, but it has to be just hard enough to be visually convincing.&lt;/p&gt;
&lt;center&gt;&lt;figure&gt;
&lt;img src=&#34;https://algebraic.games/blog/desert_space_ship/sand.png&#34;/&gt;
&lt;figcaption&gt;The default Squishy Volumes input generating modifier.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;h3 id=&#34;space-ship&#34;&gt;Space Ship&lt;/h3&gt;
&lt;p&gt;The space ship colliders were a bit tricky.
But thanks to Blender operations and modifiers we can work around the non-manifold edges and use Squishy Volumes &lt;em&gt;Collider Grid&lt;/em&gt; output to debug inconsistencies.&lt;/p&gt;
&lt;h4 id=&#34;non-manifold-edges&#34;&gt;Non-Manifold Edges&lt;/h4&gt;
&lt;p&gt;The model does have a bunch of non-manifold edges.
That is not a problem for rendering and many mesh operations, but it becomes problematic for determining whether a point is inside or outside a mesh.
Squishy Volumes needs to do just that and throws this error:&lt;/p&gt;
&lt;center&gt;&lt;figure&gt;
&lt;img src=&#34;https://algebraic.games/blog/desert_space_ship/non-manifold.png&#34;/&gt;
&lt;figcaption&gt;The Squishy Volumes non-manifold error popup.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;p&gt;To inspect the mesh, we can use this &lt;a href=&#34;https://docs.blender.org/manual/en/latest/modeling/meshes/selecting/all_by_trait.html#non-manifold&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;select non-manifold operation&lt;/a&gt;:&lt;/p&gt;
&lt;center&gt;&lt;figure&gt;
&lt;img src=&#34;https://algebraic.games/blog/desert_space_ship/select-non-manifold.png&#34;/&gt;
&lt;figcaption&gt;That is where the non-manifold operator is hiding.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;p&gt;In this case, it&amp;rsquo;s possible to leverage Blender&amp;rsquo;s &lt;a href=&#34;https://docs.blender.org/manual/en/5.0/modeling/modifiers/generate/remesh.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Remesh Modifier&lt;/a&gt; to get manifold geometry.
This is with a &lt;code&gt;Voxel Size&lt;/code&gt; of &lt;code&gt;0.05 m&lt;/code&gt;:&lt;/p&gt;
&lt;center&gt;&lt;figure&gt;
&lt;img src=&#34;https://algebraic.games/blog/desert_space_ship/remeshed.png&#34;/&gt;
&lt;figcaption&gt;Remeshing gives us a nice mesh.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;p&gt;One might be tempted to use some adaptive remeshing, alas&amp;hellip;&lt;/p&gt;
&lt;h4 id=&#34;inconsistent-collider-grid&#34;&gt;Inconsistent Collider Grid&lt;/h4&gt;
&lt;p&gt;If the collider mesh is &amp;ldquo;weird&amp;rdquo; enough, the grid embedding may become inconsistent.
This can cause all kinds of wrong behavior.&lt;/p&gt;
&lt;p&gt;In this case, I thought it would be a good idea to do some adaptive remeshing, but that caused some hefty squishing, and Squishy Volumes dialed down the adaptive timestep all the way to zero, which results in this error:&lt;/p&gt;
&lt;center&gt;&lt;figure&gt;
&lt;img src=&#34;https://algebraic.games/blog/desert_space_ship/zero-timestep.png&#34;/&gt;
&lt;figcaption&gt;Squishy Volumes gives us a cryptic error popup.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;p&gt;It&amp;rsquo;s not obvious at all what&amp;rsquo;s causing this, but if we add the collider grid as an output like this:&lt;/p&gt;
&lt;center&gt;&lt;figure&gt;
&lt;img src=&#34;https://algebraic.games/blog/desert_space_ship/output-grid-collider.png&#34;/&gt;
&lt;figcaption&gt;The Squishy Volumes operator for adding outputs.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;p&gt;We can inspect the values visually and spot some issues at the tip of the vertical stabilizer:&lt;/p&gt;
&lt;center&gt;&lt;figure&gt;
&lt;img src=&#34;https://algebraic.games/blog/desert_space_ship/inconsistent-distance.png&#34;/&gt;
&lt;figcaption&gt;The Squishy Volumes collider grid with some inconsistencies.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;p&gt;&amp;gt;.&amp;lt;&lt;/p&gt;
&lt;p&gt;It is &lt;em&gt;always&lt;/em&gt; a good idea to verify that the distance has a consistent sign (indicated by color).&lt;/p&gt;
&lt;p&gt;Removing the adaptivity of the remesh modifier resolved the issue.
The next challenge comes up during baking.&lt;/p&gt;
&lt;h2 id=&#34;baking&#34;&gt;Baking&lt;/h2&gt;
&lt;p&gt;Squishy Volumes bakes the simulation by advancing in time step-by-step.
We want the largest possible time steps for performance.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a maximum value &lt;code&gt;Time Step&lt;/code&gt; that we can specify, and with &lt;code&gt;Adpative Time Steps&lt;/code&gt; enabled, Squishy Volumes will use a heuristic to determine a (smaller) safe value.&lt;/p&gt;
&lt;center&gt;&lt;figure&gt;
&lt;img src=&#34;https://algebraic.games/blog/desert_space_ship/TimeStep.png&#34;/&gt;
&lt;figcaption&gt;The Squishy Volumes bake panel with time step settings.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;p&gt;In this scene, the heuristic failed.
A few frames into the bake, the sand explodes as the space ship pushes through it.&lt;/p&gt;
&lt;p&gt;As a workaround, we can limit the timestep to &lt;code&gt;0.0005 s&lt;/code&gt;, keeping everything stable from start to finish.&lt;/p&gt;
&lt;p&gt;It now remains to work with the output.&lt;/p&gt;
&lt;h2 id=&#34;simulation-output&#34;&gt;Simulation Output&lt;/h2&gt;
&lt;p&gt;During baking, we need a fast way to monitor progress.
And once the simulation is finished, we want to be able to play it back quickly.&lt;/p&gt;
&lt;h3 id=&#34;visualize-as-points&#34;&gt;Visualize as Points&lt;/h3&gt;
&lt;p&gt;While baking, it’s a good idea to check the output every now and then.
The default visualization modifier from Squishy Volumes lacks this, and its &lt;code&gt;Deformed Cubes&lt;/code&gt; visualization can brick Blender.
But a simple setup like this does the trick:&lt;/p&gt;
&lt;center&gt;&lt;figure&gt;
&lt;img src=&#34;https://algebraic.games/blog/desert_space_ship/simple_visualization.png&#34;/&gt;
&lt;figcaption&gt;A simple node setup for point rendering.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;center&gt;&lt;figure&gt;
&lt;img src=&#34;https://algebraic.games/blog/desert_space_ship/simple_visualization_2.png&#34;/&gt;
&lt;figcaption&gt;This visualization doesn&#39;t brick Blender.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;p&gt;Even with millions of particles, the viewport stays responsive, and we can see whether our sand has exploded yet.&lt;/p&gt;
&lt;p&gt;Once the simulation is finished, we can speed things up further.&lt;/p&gt;
&lt;h3 id=&#34;bake-to-bake&#34;&gt;Bake to Bake&lt;/h3&gt;
&lt;p&gt;The data transfer from Squishy Volumes to Blender doesn’t change after the simulation finishes.
For a given frame, Squishy Volumes needs to read the simulation results from disk, perform some data wrangling, and hand them over to Blender via the “Rust-&amp;gt;Python-&amp;gt;C++” bridge.&lt;/p&gt;
&lt;p&gt;That tends to be slow, and in this scene, it took &lt;em&gt;seconds per frame&lt;/em&gt;.
To add to the issue, the synchronization is driven by a frame-change handler that “magically” overwrites the geometry on the output object.&lt;/p&gt;
&lt;p&gt;By utilizing Blender&amp;rsquo;s native bakes, we can alleviate these issues.
We do add a manual step, though.&lt;/p&gt;
&lt;p&gt;We can put this as the first modifier on the output:&lt;/p&gt;
&lt;center&gt;&lt;figure&gt;
&lt;img src=&#34;https://algebraic.games/blog/desert_space_ship/bake_node.png&#34;/&gt;
&lt;figcaption&gt;A minimal bake node setup does the trick.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;p&gt;And then bake the Squishy Volumes bake into a Blender bake.
This still needs to step through the frames and fetch the data in the same way as before, but it&amp;rsquo;s not being displayed while baking, and afterwards, &lt;em&gt;it plays back fast&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We can now disable &lt;code&gt;Sync&lt;/code&gt; on our simulation, which skips that whole data business:&lt;/p&gt;
&lt;center&gt;&lt;figure&gt;
&lt;img src=&#34;https://algebraic.games/blog/desert_space_ship/sync.png&#34;/&gt;
&lt;figcaption&gt;The Squishy Volumes overview panel where we can disable syncing.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;p&gt;And in fact, we could disable Squishy Volumes completely now and get rid of the huge cache directory.&lt;/p&gt;
&lt;p&gt;There are two things to keep in mind with this workflow.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We need to rebake the bake-to-bake each time we re-simulate&lt;/li&gt;
&lt;li&gt;The &lt;a href=&#34;https://docs.blender.org/manual/en/5.0/modeling/modifiers/geometry_nodes.html#bake&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;Bake Target&lt;/code&gt;&lt;/a&gt; should be &lt;code&gt;Disk&lt;/code&gt;, otherwise saving (and autosaving!) is slow.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;But with this workflow, it can be quite satisfying to look at the animated result and inspect it from all angles, and it&amp;rsquo;s a real enabler for the creative work that follows.&lt;/p&gt;
&lt;h1 id=&#34;visualization&#34;&gt;Visualization&lt;/h1&gt;
&lt;p&gt;We’re done with challenges; in this part, we just talk about visualization tricks.&lt;/p&gt;
&lt;p&gt;The sand is displayed as deformed cubes from the default visualization, but with a different material that has some sand colors and subsurface scattering.&lt;/p&gt;
&lt;h2 id=&#34;fake-floor&#34;&gt;Fake Floor&lt;/h2&gt;
&lt;p&gt;To smooth out the boundary between the actually simulated material and the static floor, we can add another “fake” surface.
That surface has some purely geometric cubes scattered on it with the same look as our simulated ones.&lt;/p&gt;
&lt;center&gt;&lt;figure&gt;
&lt;img src=&#34;https://algebraic.games/blog/desert_space_ship/fake_floor.png&#34;/&gt;
&lt;figcaption&gt;Conceal the awkward input shape.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;h2 id=&#34;dust-volume&#34;&gt;Dust Volume&lt;/h2&gt;
&lt;p&gt;Another trick worth noting here is to use a second particle output for the “dust”.
For this second output, we use the basic surface reconstruction with scaled-up radii and convert it to a low-density volume.
This way, it looks like the kicked-up sand produces dust and the spotlight&amp;rsquo;s cone becomes a bit visible.&lt;/p&gt;
&lt;center&gt;&lt;figure&gt;
&lt;img src=&#34;https://algebraic.games/blog/desert_space_ship/dust.png&#34;/&gt;
&lt;figcaption&gt;The surface resconstruction can be used as a light-scattering volume.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/center&gt;
&lt;p&gt;Bonus trick: The &lt;code&gt;Squishy Volumes Restrict View&lt;/code&gt; object operation allows you to view a selected region of material, which is very useful.&lt;/p&gt;
&lt;p&gt;Then there are other topics, like the background and world shader, that we could discuss, but they are not directly related to Squishy Volumes and are much better covered elsewhere.&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Here we go over the challenges again and discuss how Squishy Volumes could make them less challenging.&lt;/p&gt;
&lt;p&gt;The error handling of the &lt;a href=&#34;#non-manifold-edges&#34;&gt;non-manifold&lt;/a&gt; issue might be good enough as it is.
Blender provides tools to find and fix this issue, and it seems hard to fix automatically.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;#inconsistent-collider-grid&#34;&gt;inconsistency of the collider grid&lt;/a&gt; is something we can hopefully improve!
It would be helpful to have some sort of automatic sanity check at the very least.
This specific issue might also be a bug and shouldn&amp;rsquo;t have happened in the first place.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;#baking&#34;&gt;Unintended explosions&lt;/a&gt; are annoying, especially because they tend to occur some time into the bake.
And the workaround of manually setting a low maximum can hurt performance.
I suspect that the time-step heuristic needs to be changed.
Needs more investigation.&lt;/p&gt;
&lt;p&gt;Adding the cheap &lt;a href=&#34;#visualize-as-points&#34;&gt;point visualization&lt;/a&gt; to the default modifier is a no-brainer.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;#bake-to-bake&#34;&gt;workflow&lt;/a&gt; utilizing Blender&amp;rsquo;s bakes is very exciting!
Maybe Squishy Volumes can offer more tooling around it to reduce the number of manual steps.
And maybe the data transfer could also be improved a bit; it still takes some time for the bake to record all the frames.&lt;/p&gt;
&lt;p&gt;And of course, there are many more ways in which Squishy Volumes can and will improve that we didn’t discuss.
One important one is overall performance.
It&amp;rsquo;s still punishing to restart a simulation to try any different input.
(GPU support, pls)&lt;/p&gt;
&lt;p&gt;Finally, I think the result looks not too shabby!
It is really cool what Squishy Volumes enables us to do, despite the challenges.
This scene sets the bar for future Squishy Volume projects.&lt;/p&gt;
&lt;p&gt;Try creating a scene with Squishy Volumes and join our &lt;a href=&#34;https://discord.com/invite/P8mrnQEavf&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Discord&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;We are happy to help you out. &amp;lt;3&lt;/p&gt;
&lt;p id=&#34;footnote-1&#34;&gt;
[1] This is currently the only supported mode. In the future, there will also be GPU support and implicit integration.&lt;/p&gt;
&lt;p id=&#34;footnote-2&#34;&gt;
[2] Usually, when I&#39;m posting something, for example on &lt;a href=&#34;https://discord.com/channels/1336035911766511687/1418370752830242886&#34;&gt;Discord&lt;/a&gt;, I like to share the &lt;tt&gt;.blend&lt;/tt&gt; file.
In this case the &lt;a href=&#34;https://itch.io/blog/929708/general-paid-asset-license&#34;&gt;license&lt;/a&gt; of &lt;a href=&#34;https://tarasov3d-studio.itch.io/sci-fi-ship-lowpoly-3d-model&#34;&gt;the space ship model&lt;/a&gt; prohibits this.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Making of Tree Clipper</title>
      <link>https://algebraic.games/blog/tree_clipper/</link>
      <pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate>
      <guid>https://algebraic.games/blog/tree_clipper/</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Tree Clipper is an Add-on and library for exporting and importing 
 as 
.&lt;/p&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;
&amp;rsquo; development has been on hold for the last couple of months in favor of 
.
I am very proud to say that Tree Clipper is now ready for use and has been accepted on the official extension platform as well.
Big thanks to the contributors, especially Brady Johnston and Jan-Hendrik Müller!&lt;/p&gt;
&lt;p&gt;This blog entry provides motivation and an overview of the features, but is mostly about the technical challenges we faced while making Tree Clipper.&lt;/p&gt;
&lt;p&gt;Check out the 
!&lt;/p&gt;
&lt;h2 id=&#34;motivation&#34;&gt;Motivation&lt;/h2&gt;
&lt;p&gt;Blender&amp;rsquo;s node trees are arguably a visual scripting language.
Yet, the usual software development tools and workflows do not apply as it&amp;rsquo;s a &lt;em&gt;graph-based&lt;/em&gt; language rather than a &lt;em&gt;text-based&lt;/em&gt; one.
You can see where I&amp;rsquo;m going here: Tree Clipper changes that.&lt;/p&gt;
&lt;p&gt;And JSON is a handy text-based format because many tools in many languages support it.
If only it had trailing commas.&lt;/p&gt;
&lt;p&gt;(╯°□°)╯︵ ┻━┻&lt;/p&gt;
&lt;p&gt;From my perspective, a software developer&amp;rsquo;s perspective, this is already ample motivation.
But wait, there&amp;rsquo;s more:
It&amp;rsquo;s actually really easy to go from JSON to a compressed (Base64) string and back!
This provides a &lt;em&gt;convenient&lt;/em&gt; way to share that is useful for any Blender node tree user.
(Not only add-on devs)&lt;/p&gt;
&lt;p&gt;Shoutout to Brady Johnston for this idea!&lt;/p&gt;
&lt;p&gt;If you have ever played 
 with any vigor, you have also encountered its 
 and maybe also one of the unofficial sharing websites, like 
.
Basically, Blueprints are also compressed strings that enable players to copy and share designs via &lt;em&gt;the system clipboard&lt;/em&gt;.
Need a 
? Just copy from the browser &amp;amp; paste in-game without losing a beat.&lt;/p&gt;
&lt;p&gt;So that is another angle to look at Tree Clipper: it enables us to do a 
 with Blender&amp;rsquo;s node trees.
Imagine the &lt;em&gt;speed&lt;/em&gt; with which we could exchange ideas, teach, and debug.
Not to mention that we can analyze and search JSON with lean tools without depending on the Blender executable.&lt;/p&gt;
&lt;p&gt;The potential is exciting!&lt;/p&gt;
&lt;h2 id=&#34;why-not-use-&#34;&gt;Why Not Use &amp;lt;..&amp;gt;?&lt;/h2&gt;
&lt;p&gt;Yes! Depending on what we want, there are better solutions.&lt;/p&gt;
&lt;p&gt;For example, &lt;em&gt;binary&lt;/em&gt; &lt;tt&gt;.blend&lt;/tt&gt; files might be better suited.
Especially, if we&amp;rsquo;re also interested in exporting/importing meshes or other large data.
Node trees can be loaded from a &lt;tt&gt;.blend&lt;/tt&gt; file 
.
And there&amp;rsquo;s the 
, which offers a nice set of workflows.
These can also leverage Blender&amp;rsquo;s own compatibility system. Nice!&lt;/p&gt;
&lt;p&gt;Given that we&amp;rsquo;re interested in a &lt;em&gt;text&lt;/em&gt; format, we&amp;rsquo;re out of luck when it comes to official support.
There is no official JSON (or any text-based) export/import of nodes, and a relatively recent 
 for this was rejected.&lt;/p&gt;
&lt;p&gt;However, there are a few add-ons that are similar to Tree Clipper.
Let me refer to the 
 section of the README on GitHub.&lt;/p&gt;
&lt;h2 id=&#34;features&#34;&gt;Features&lt;/h2&gt;
&lt;p&gt;Alright!
Tree Clipper&amp;rsquo;s purpose is to export and import nodes as JSON.
It does that. Trees, subtrees, interfaces, nodes, links, sockets, the whole shebang.&lt;/p&gt;
&lt;h3 id=&#34;basic-workflow&#34;&gt;Basic Workflow&lt;/h3&gt;
&lt;p&gt;We get a panel in the (geometry/material/compositor/texture) node editor that looks like this:
&lt;img src=&#34;https://algebraic.games/blog/tree_clipper/basic_ui.png&#34;/&gt;&lt;/p&gt;
&lt;h4 id=&#34;exporting&#34;&gt;Exporting&lt;/h4&gt;
&lt;p&gt;This is the basic pop-up for exporting (the current tree and subtrees are exported):
&lt;img src=&#34;https://algebraic.games/blog/tree_clipper/basic_export_ui.png&#34;/&gt;&lt;/p&gt;
&lt;p&gt;We make two choices here.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Either save directly into the &lt;strong&gt;Clipboard&lt;/strong&gt; or to a &lt;strong&gt;File&lt;/strong&gt; on disk.
This is needed because (very) large setups do not fit into the system clipboard.&lt;/li&gt;
&lt;li&gt;For sharing, the &lt;strong&gt;Compress&lt;/strong&gt;ed Base64 string is more convenient, but plain &lt;strong&gt;JSON&lt;/strong&gt; is preferable for add-on development.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here are two export examples:&lt;/p&gt;
&lt;p&gt;Compressed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;TreeClipper::H4sIAAQhjmkC/+1XbW/iOBD+K1U+rxDZAi33rarQCh0Lqy2ctFpVlnGG4KtjW7YD7VX89xs7CTg0x+2edLrVafuBwrw4M8/MM568JmsBMgNDdmAsVzL55SoZ9vq9NHl3lTgDQJjgWrcNUN0begOpMiDeyqL462vCM/zfR0VGHcWvr4mkBXiXD6AKcOblao4u1vuWFsiGPgHBLwZNNlRYqOXw7AxtFM6UXs4tMaV0vABSn954OJpHv5gSypBKlswX84l/WAYbWgpHcqNKTULYe565LdqkgxAwWGa4dnWG3mctfAS1C5cOzIYyiAKKRHXqaZw6ZY7vAD0zeK513EFhA2Ann/exTzCIsLy+hGXSHbdV7AkccS86WHvAH4Io9ttyhGBHRQkRdEHIJSlUxje8VZWNMgwQOElQIbJIg3Xh0mpgjqjS6dK1dZpKEMSpPBfxowR9wZ6yINCPd5xagCyxDdA7g1iuQqpUEEHXICIFdc7wdemwO1RBeUDj02I6X8blPxk1WAbAsKDMlQaOmN2tlovYj8sqr+S3u9kqNBSipCrRYrX8tApP8cU7HvGwuP91skwOh3dNoQc/a/mj13I6v1jKx8PB1zOMPXti8PAt6/HrqOF7ROdRbCkUo3Wtv47f93s4hm7x89EjWqsIXVslMMcOm2h+eXGyBZ5vfRZpvxIcW8wPsKtpSDtUqwI7wLWnRnKZE22UpnkTTnI3mzUjmpXWqYKEqXo+ZX1U/d6ofzPGv9v+YDAe3YwQjW+T+SSqponOtVu1J1Vj2NOoDVJtYMdhf9bjcZ9VQLWOcniVYDfEfeyBiKp30zl/H0OhKxJExreXhvX4HxD8LAWQFC/k7JS54PKJCF5wD9KgPx42iXXwqXMOaOzunP9RqPjCrBv7w2TxcbL8/CXExq1GIhO7pZVyNsWrMxpfaee9/iOmdb96WC4+diZ1P/18P5tUVK6ueJ41qfjhGrhypIqmBqSPUJZCxFCkf8VjJOPfE/nc6DuZvKgm808qv6FyenGXSq//bX6m/x05B/8/cr4ZvunwwqjupvKJKy0uV+tMvaAfNx2ffudkaHrEH7v0y7vfUaRUrmFZfaYHLo53dLEd2xeP9SjyrPW+41kQg70xSN9qJayvG6dOv9PrJnh0dUqJ9vIWLYLRE/ybkFr/3mZuo4CMd4ktK4XuUmjc1c7kvuykALvtELPS7KBDrhUew4Qqs7PXwj3ljuD26t9J2VOHZ26Aop0Gybg4nx8NAKSgkuZQbbOnsnsmPOMbHS6kviIeR8tAVove4fAn7fQnsCoPAAA=
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;JSON:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-gdscript3&#34; data-lang=&#34;gdscript3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;blender_version&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;5.0.1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tree_clipper_version&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;0.1.5&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;node_trees&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Geometry Nodes&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;use_fake_user&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;use_extra_user&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_runtime_data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tag&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;color_tag&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;NONE&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;default_group_node_width&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;140&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;bl_use_group_interface&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;interface&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;active_index&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;items_tree&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;items&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Geometry&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;socket_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;NodeSocketGeometry&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;hide_value&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;hide_in_modifier&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;force_non_field&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_inspect_output&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_panel_toggle&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;layer_selection_field&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;menu_expanded&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;optional_label&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;attribute_domain&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;POINT&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;default_attribute_name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;structure_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;AUTO&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;default_input&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;VALUE&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;in_out&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;OUTPUT&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;item_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;SOCKET&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Geometry&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;socket_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;NodeSocketGeometry&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;hide_value&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;hide_in_modifier&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;force_non_field&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_inspect_output&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_panel_toggle&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;layer_selection_field&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;menu_expanded&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;optional_label&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;attribute_domain&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;POINT&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;default_attribute_name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;structure_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;AUTO&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;default_input&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;VALUE&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;in_out&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;INPUT&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;item_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;SOCKET&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;nodes&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;active&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;items&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;location&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;920.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;80.0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;location_absolute&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;920.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;80.0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;width&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;140.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;height&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;100.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Group Input&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;label&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;warning_propagation&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;ALL&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;use_custom_color&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;color&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.6079999804496765&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.6079999804496765&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.6079999804496765&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;select&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;show_options&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;show_preview&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;hide&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;mute&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;show_texture&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;inputs&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;items&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;outputs&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;items&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Geometry&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;hide&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;enabled&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;link_limit&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4095&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;show_expanded&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;hide_value&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;pin_gizmo&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;GEOMETRY&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;display_shape&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;LINE&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;hide&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;enabled&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;link_limit&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4095&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;show_expanded&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;hide_value&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;pin_gizmo&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;CUSTOM&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;display_shape&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;CIRCLE&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;bl_idname&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;NodeGroupInput&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;parent&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;location&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1400.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;80.0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;location_absolute&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1400.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;80.0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;width&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;140.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;height&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;100.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Group Output&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;label&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;warning_propagation&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;ALL&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;use_custom_color&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;color&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.6079999804496765&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.6079999804496765&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.6079999804496765&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;select&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;show_options&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;show_preview&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;hide&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;mute&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;show_texture&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;inputs&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;items&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Geometry&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;hide&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;enabled&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;link_limit&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;show_expanded&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;hide_value&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;pin_gizmo&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;GEOMETRY&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;display_shape&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;LINE&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;hide&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;enabled&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;link_limit&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4095&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;show_expanded&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;hide_value&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;pin_gizmo&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;CUSTOM&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;display_shape&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;CIRCLE&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;outputs&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;items&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;bl_idname&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;NodeGroupOutput&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;parent&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_active_output&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;true&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;bl_idname&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;GeometryNodeTree&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;annotation&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;links&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;items&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_valid&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_muted&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;from_socket&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;to_socket&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;13&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_tool&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_modifier&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_mode_object&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_mode_edit&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_mode_sculpt&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_mode_paint&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_type_mesh&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_type_curve&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_type_pointcloud&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;use_wait_for_click&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;is_type_grease_pencil&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;show_modifier_manage_panel&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;true&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;external&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{},&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;scenes&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;importing&#34;&gt;Importing&lt;/h4&gt;
&lt;p&gt;The import adds the tree as a group node by default:
&lt;img src=&#34;https://algebraic.games/blog/tree_clipper/basic_import.png&#34;/&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it, byeee ~&lt;/p&gt;
&lt;h3 id=&#34;external-items&#34;&gt;External Items&lt;/h3&gt;
&lt;p&gt;Ok, there&amp;rsquo;s a good chance that the basic features are plenty enough for us, but what if we reference &amp;ldquo;external&amp;rdquo; items like other objects or materials or collections or images or &amp;hellip; 
&lt;/p&gt;
&lt;p&gt;There are no external items in the basic example above, but have a look at this one and notice the additional UI in the export pop-up:&lt;/p&gt;
&lt;img src=&#34;https://algebraic.games/blog/tree_clipper/advanced_export_ui.png&#34;/&gt;
&lt;p&gt;Now, these references to objects, materials, etc., could be vital to the setup&amp;rsquo;s function!
But we&amp;rsquo;ve drawn our line in the sand; those can&amp;rsquo;t be in the export.
The next best thing we found is to provide the export user with the means to create an &lt;em&gt;import interface&lt;/em&gt; of sorts.
Meaning, the exporter can provide a &amp;ldquo;hint&amp;rdquo; on what the item should be set to on the importing side.&lt;/p&gt;
&lt;p&gt;For the importer, it looks like this:
&lt;img src=&#34;https://algebraic.games/blog/tree_clipper/advanced_import_ui.png&#34;/&gt;&lt;/p&gt;
&lt;p&gt;These listed items can then be filled by the already existing objects, images, collections, etc., in the importer&amp;rsquo;s Blender project before running the actual import.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it for real, bye.&lt;/p&gt;
&lt;h3 id=&#34;backwards-compatibility&#34;&gt;Backwards Compatibility&lt;/h3&gt;
&lt;p&gt;( ͡° ͜ʖ ͡°)&lt;/p&gt;
&lt;p&gt;Starting from Blender 5, which is also the minimum version, we have backwards compatibility!
Tree Clipper would be much less useful without it.&lt;/p&gt;
&lt;p&gt;At the moment, it&amp;rsquo;s really just getting things from 5.0 to 5.1, but that worked out pretty good.
For example, the 
, and Tree Clipper does what you&amp;rsquo;d expect:&lt;/p&gt;
&lt;img src=&#34;https://algebraic.games/blog/tree_clipper/backwards_compatibility.png&#34;/&gt;
&lt;h3 id=&#34;use-in-other-addons&#34;&gt;Use in Other Addons&lt;/h3&gt;
&lt;p&gt;Alright, one more &lt;em&gt;key&lt;/em&gt; use case for Tree Clipper.&lt;/p&gt;
&lt;p&gt;The tree exports can be shipped with other add-ons, which can then import these setups for their users.
This is possible because the core logic is a separate 
 available on 
.&lt;/p&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;Other add-ons can also define additional properties on the built-in nodes or define new node types altogether.
It’s expected that third-party add-on devs will either overwrite the existing &lt;em&gt;handlers&lt;/em&gt; or provide additional &lt;em&gt;handlers&lt;/em&gt; for their custom types.
More on this in the 
.&lt;/p&gt;
&lt;p&gt;If &lt;em&gt;you&lt;/em&gt; are an add-on developer and would be interested in using Tree Clipper in that way, HMU!
There&amp;rsquo;s little to no documentation on how to use the core logic, and we&amp;rsquo;re still figuring out what the API should look like.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s about all I wanted to point out feature-wise.
Come stick around and see how it works under the hood!&lt;/p&gt;
&lt;h2 id=&#34;how-it-works&#34;&gt;How it Works&lt;/h2&gt;
&lt;p&gt;How it Works? How it Works.&lt;/p&gt;
&lt;p&gt;So, this might be obvious, but we&amp;rsquo;re limited to the Python API for exporting and importing node trees.
The task is to scrape all the information we need to recreate a given node tree
And good news: 
 the information we need to recreate is available via the Python API!&lt;/p&gt;
&lt;p&gt;And (almost) all (except for 
), information can be scraped and recreated in the same way.&lt;/p&gt;
&lt;p&gt;We’ll glance at exporting, look at the handlers used for exporting and importing, and see that importing is much more involved than exporting.&lt;/p&gt;
&lt;h3 id=&#34;exporting-1&#34;&gt;Exporting&lt;/h3&gt;
&lt;p&gt;To export, we feed the (root) tree to a recursive function (
), which takes a Python object and populates its JSON representation with the results of recursive exports of its properties.
That&amp;rsquo;s the basic idea.&lt;/p&gt;
&lt;p&gt;The 
 are limited to simple data types (bools, ints, floats, strings, and enums, which are also strings), pointers to other Python objects, and collections of Python objects.
That might sound like a lot of properties, but it really narrows down what Tree Clipper must be able to handle, and all these properties map nicely to JSON!&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a schematic of what the structure of a tree looks like from the Python API:&lt;/p&gt;
&lt;img src=&#34;https://algebraic.games/blog/tree_clipper/example_structure.svg&#34;/&gt;
&lt;p&gt;The point here is that we can reach all of the interesting stuff by following the arrows.
In some cases, we can get there by taking multiple paths.
For example, the sockets of a link are always part of the inputs or outputs of the respective node.
The depth of this structure isn&amp;rsquo;t bounded; there can always be another subtree within a 
.&lt;/p&gt;
&lt;p&gt;Depending on the type of object, e.g., 
, we want to export different things.
An exported link looks something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-JSON&#34; data-lang=&#34;JSON&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;199&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;is_valid&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;is_muted&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;from_socket&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;to_socket&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;75&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each exported object gets an &lt;tt&gt;id&lt;/tt&gt; and &lt;tt&gt;data&lt;/tt&gt;, and in this case, the pointers to the sockets have been serialized beforehand and are referenced by the &lt;tt&gt;id&lt;/tt&gt; they received at that time.
Checking the 
, there are a few things that are skipped because we don’t need them to reconstruct the link.
&lt;tt&gt;is_valid&lt;/tt&gt; and &lt;tt&gt;is_muted&lt;/tt&gt; should also be skipped if they&amp;rsquo;re set to the default.
Optimization for the future.&lt;/p&gt;
&lt;p&gt;In any case, we want to specify handling for certain types while retaining a default export for most cases.
There are, after all, 500+ different node types, and most fall into the “simple” category.&lt;/p&gt;
&lt;p&gt;So, we&amp;rsquo;ve opted to build a general recursive algorithm that can take care of the &amp;ldquo;simple types&amp;rdquo; while still allowing us to take the reins and specify some special export logic for types we deem &amp;ldquo;complex&amp;rdquo;.
The type-specific code is contained in the respective &lt;em&gt;handler&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&#34;handlers&#34;&gt;Handlers&lt;/h3&gt;
&lt;p&gt;Passing a handler is the way to override what Tree Clipper does for a given type.
Upon construction, the exporter (and importer as well) is 
, and for each object the exporter (or importer) encounters, it first tries to find the best match in that dictionary.&lt;/p&gt;
&lt;p&gt;The best match could be a &lt;em&gt;base class&lt;/em&gt; of that type.
Here&amp;rsquo;s the 
 that finds the most specific handler for a given type.
And this enables us to define a partial handling of a type.&lt;/p&gt;
&lt;p&gt;There is a set of properties already in the base class; those are considered &lt;em&gt;handled&lt;/em&gt;, while the remaining properties of the actual type are considered &lt;em&gt;unhandled&lt;/em&gt;.
For the unhandled properties, a general export/import is applied.

 in the export and this 
 in the import.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s worth noting that if there is a handler for a more general base class and also a more specific base class for a given type, the more general handler is completely ignored.
That is a double-edged sword: we get complete control over specific types, but we also need to repeat ourselves for common things.&lt;/p&gt;
&lt;p&gt;Tree Clipper provides the 
, which handle the special cases for Blender&amp;rsquo;s built-in nodes.
(In that same file, the handlers are defined)
The idea is that third-party add-ons with custom node types can modify it.&lt;/p&gt;
&lt;h3 id=&#34;importing-1&#34;&gt;Importing&lt;/h3&gt;
&lt;p&gt;Importing is much more involved than exporting.&lt;/p&gt;
&lt;p&gt;But first, let&amp;rsquo;s establish the similarities to the export.
We also &amp;ldquo;follow the arrows&amp;rdquo; in a general recursive function (
).
And we also use handlers to specify logic for specific types.&lt;/p&gt;
&lt;p&gt;The main difference is that we&amp;rsquo;re recreating and filling the structures as we go.
Given a tree in JSON form, we start by adding a new node group, flesh out the interface, add the nodes one by one, and finally establish the links.

&lt;/p&gt;
&lt;p&gt;Apart from the order, there&amp;rsquo;s also this &lt;em&gt;nasty&lt;/em&gt; pitfall to avoid: 
.
And we are modifying containers all the time while importing.
We&amp;rsquo;re adding trees, nodes, and links.&lt;/p&gt;
&lt;h4 id=&#34;getters&#34;&gt;Getters&lt;/h4&gt;
&lt;p&gt;To avoid this pitfall, there are 
s in Tree Clipper.
They are callables without arguments and return a &amp;ldquo;fresh&amp;rdquo; (and therefore safe) reference to the object we&amp;rsquo;re currently importing.
The getter is passed down the recursion and is &amp;ldquo;wrapped&amp;rdquo; at each level.&lt;/p&gt;
&lt;p&gt;Here we&amp;rsquo;re descending into a property with an &lt;tt&gt;identifier&lt;/tt&gt; and wrapping the getter accordingly.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Python&#34; data-lang=&#34;Python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_import_obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;getter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;lambda&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;getattr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;identifier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;serialization&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;serialization&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;from_root&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And here, we&amp;rsquo;re iterating over the items of a collection and importing them, one by one.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Python&#34; data-lang=&#34;Python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;make_getter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GETTER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;lambda&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;getattr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;identifier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;item&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;enumerate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;serialized_items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DATA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;NAME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;unnamed&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_import_obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;getter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;make_getter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;serialization&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;serialized_items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;from_root&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_root&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;[&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;] (&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The importer also tracks getters for &lt;em&gt;all&lt;/em&gt; imported objects, which can be looked up by the &lt;tt&gt;id&lt;/tt&gt; &lt;em&gt;assigned by the exporter&lt;/em&gt;.
That is how Tree Clipper can recreate links!&lt;/p&gt;
&lt;h4 id=&#34;order-order&#34;&gt;Order, Order!&lt;/h4&gt;
&lt;p&gt;The order in which we import stuff is important.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s jump right to one of the most complicated cases: 
.
Consider this setup, we&amp;rsquo;re able to define a default for the interface item &amp;ldquo;Menu&amp;rdquo;:
&lt;img src=&#34;https://algebraic.games/blog/tree_clipper/default_set.png&#34;/&gt;
However, since the possible values of this default are defined by the &amp;ldquo;Menu Switch&amp;rdquo; node on the right, we lose the ability to define a default if the connection is broken:
&lt;img src=&#34;https://algebraic.games/blog/tree_clipper/no_default_possible.png&#34;/&gt;&lt;/p&gt;
&lt;p&gt;That means, to faithfully recreate the former setup with the default defined, Tree Clipper needs to order the import as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Import the interface so that the sockets appear on the “Group Input”.&lt;/li&gt;
&lt;li&gt;Import all the nodes, including their properties, so that the right side of the &amp;ldquo;Repeat&amp;rdquo; zone has a &amp;ldquo;Menu&amp;rdquo; entry and the &amp;ldquo;Menu Switch&amp;rdquo; has the enum variants. The sockets are implicitly created.&lt;/li&gt;
&lt;li&gt;Pair the two ends of &amp;ldquo;Repeat&amp;rdquo;. Then the left side also gets the &amp;ldquo;Menu&amp;rdquo; socket.&lt;/li&gt;
&lt;li&gt;Import all the links between the sockets.&lt;/li&gt;
&lt;li&gt;Finally, set the default value for the &amp;ldquo;Menu&amp;rdquo; in the tree interface.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Rest assured, there are more tricky-icky things, like the ordering of interface items and multi-links, that need some special attention.
But we’ll leave it at this example.&lt;/p&gt;
&lt;h3 id=&#34;testing&#34;&gt;Testing&lt;/h3&gt;
&lt;p&gt;After implementing Tree Clipper, I&amp;rsquo;d personally have no trust in a solution that doesn&amp;rsquo;t have automatic tests.
We have 🌟extensive🌟 tests for Tree Clipper.
There were so. many. bugs.&lt;/p&gt;
&lt;img src=&#34;https://algebraic.games/blog/tree_clipper/tests.png&#34;/&gt;
&lt;p&gt;We have generated tests, round-trip tests for really complex setups from community &lt;tt&gt;.blend&lt;/tt&gt; files, and tests for specific bugs we&amp;rsquo;ve found and fixed.&lt;/p&gt;
&lt;p&gt;Of course, that doesn&amp;rsquo;t mean that there aren&amp;rsquo;t any bugs.
In fact, I&amp;rsquo;d put some big money on bugs!
Bugs are always a good bet.&lt;/p&gt;
&lt;h2 id=&#34;future-work&#34;&gt;Future Work&lt;/h2&gt;
&lt;p&gt;Tree Clipper is just starting to get some traction in the Blender community. I hope this section will quickly go out of date :)&lt;/p&gt;
&lt;h3 id=&#34;compositor-nodes&#34;&gt;Compositor Nodes&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;Importing compositor nodes is less fun than it should be.&lt;/p&gt;
&lt;p&gt;Have a look at the 
; this part fuels my nightmares:&lt;/p&gt;



  
  &lt;blockquote class=&#34;border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6&#34;&gt;
    &lt;p&gt;Additional outputs for any enabled render passes.&lt;/p&gt;

  &lt;/blockquote&gt;

&lt;p&gt;That means that the render passes of the 
, which is an external item, define the sockets of that node.
Ugh.&lt;/p&gt;
&lt;p&gt;So the importer needs to have a scene where the render passes match those the exporter used.&lt;/p&gt;
&lt;p&gt;Currently, Tree Clipper exports that information and complains if the provided scene&amp;rsquo;s render passes don&amp;rsquo;t match, but it&amp;rsquo;s really annoying to fix it.
Maybe Tree Clipper can adjust the scene to fit.
That could be an unexpected change to the user of the import, though.&lt;/p&gt;
&lt;p&gt;It may be acceptable if there were a pop-up warning 🤔&lt;/p&gt;
&lt;h3 id=&#34;import-performance&#34;&gt;Import Performance&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;The other issue is that large node trees can take a long time to import.&lt;/p&gt;
&lt;p&gt;Regrettably, this is not something that will go away quickly.
It is a broader performance issue in how Blender verifies nodes added via Python.
The more nodes there are in a group, the longer verification takes, so the runtime is quadratic in the number of nodes in a given group.&lt;/p&gt;
&lt;p&gt;The workaround is to divvy up the nodes into more groups before exporting.&lt;/p&gt;
&lt;h3 id=&#34;key-framed-values&#34;&gt;Key-Framed Values&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;This is a blind spot Tree Clipper still has &amp;gt;.&amp;lt;
&lt;img src=&#34;https://algebraic.games/blog/tree_clipper/animated_socket.png&#34;/&gt;
The Rotation socket in this Transform Geometry node is animated, but Tree Clipper will export whatever its current value is.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Developing Tree Clipper was more fun than it had any right to be.
I&amp;rsquo;ve personally learned a lot about Python.
Even though I do love Rust for its static typing and logic, it was refreshing to have almost none of that.&lt;/p&gt;
&lt;p&gt;Tree Clipper is going to power the generation of Squishy Volumes&amp;rsquo; node setups in the next release.
And so far it&amp;rsquo;s going really great!&lt;/p&gt;
&lt;p&gt;I hope that Tree Clipper can be useful to you as well &amp;lt;3&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>BCON 2025</title>
      <link>https://algebraic.games/blog/bcon_2025/</link>
      <pubDate>Mon, 29 Sep 2025 00:00:00 +0000</pubDate>
      <guid>https://algebraic.games/blog/bcon_2025/</guid>
      <description>&lt;figcaption&gt;
  The Logo is from the &lt;a href=&#34;https://conference.blender.org/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Blender Conference 2025&lt;/a&gt;,
  licensed under &lt;a href=&#34;https://creativecommons.org/licenses/by-sa/3.0/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;CC BY-SA 3.0&lt;/a&gt;.
&lt;/figcaption&gt;
&lt;p&gt;TL;DR:
It was a blast, three days of excitement and inspiration; if you&amp;rsquo;re considering attending next year, do it!
Here&amp;rsquo;s the recording of my talk about Squishy Volumes:&lt;/p&gt;
&lt;iframe width=&#34;420&#34; height=&#34;315&#34; allowfullscreen
  src=&#34;https://www.youtube.com/embed/hokCoVUSqAI&#34;&gt;
&lt;/iframe&gt;
&lt;p&gt;Algebraic&amp;rsquo;s &lt;a href=&#34;https://algebraic.games/squishy_volumes/&#34;&gt;Squishy Volumes&lt;/a&gt; is an extension for &lt;a href=&#34;https://www.blender.org/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Blender&lt;/a&gt; that allows you to simulate squishy stuff through the power of the Material Point Method (MPM)!
We suspected that we were flying under the radar of potentially interested Blender users. To get our extension out in the open, I attended the &lt;a href=&#34;https://conference.blender.org/2025/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Blender Conference (BCON)&lt;/a&gt; and gave a talk about it!
Indeed, just a couple of days after the conference, we had new users eagerly creating their own simulations and exploring the limits of Squishy Volumes!&lt;/p&gt;
&lt;p&gt;It was my first time at the BCON.
I was definitely nervous, not only because presenting tends to be nerve-racking, but also because I had yet to meet the attendees IRL.
Shoutout to &lt;a href=&#34;https://discord.gg/SgegYQmk&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;blender.science&lt;/a&gt; Discord server!
Connecting beforehand gave me much-needed confidence.&lt;/p&gt;
&lt;p&gt;Any worries I had were (of course) completely unfounded because the Blender community is absolutely fantastic.
I experienced an intense mix of collaboration and shared fascination, met so many awesome people, and it was packed with really cool art!
The &lt;a href=&#34;https://www.amerpodia.nl/en/our-venues/felix-meritis/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;venue itself&lt;/a&gt; was also beautiful.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&#34;https://algebraic.games/blog/bcon_2025/DSC06565.JPG&#34;/&gt;
  &lt;figcaption&gt;
    Photo by &lt;a href=&#34;https://www.linkedin.com/in/raymond-neoh-a566567/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Raymond D. Neoh&lt;/a&gt;, 
    &lt;a href=&#34;https://conference.blender.org/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Blender Conference 2025&lt;/a&gt;,
    licensed under &lt;a href=&#34;https://creativecommons.org/licenses/by/4.0/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;CC BY 4.0&lt;/a&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;My talk about Squishy Volumes and the extension itself was really well received.
This feedback is obviously very motivating.
The fingers are itching to make tutorials, add more features, improve usability, and maybe most pressing: implement GPU acceleration!&lt;/p&gt;
&lt;p&gt;While the main goal was to share Squishy Volumes, many of the other talks were very interesting to me.
Highly important are the recent updates in the realm of physics simulation within Blender itself and this related talk about Hair Simulation:&lt;/p&gt;
&lt;iframe width=&#34;420&#34; height=&#34;315&#34; allowfullscreen
  src=&#34;https://www.youtube.com/embed/sfUDDKSx-1c&#34;&gt;
&lt;/iframe&gt;
&lt;p&gt;We will follow this development closely.
At &lt;em&gt;some&lt;/em&gt; point it may be possible to integrate MPM (or Squishy Volumes) into &lt;a href=&#34;https://docs.blender.org/manual/en/latest/modeling/geometry_nodes/index.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Geometry Nodes&lt;/a&gt; directly!
The future will tell.
And while it is currently impossible for extensions to define new nodes, there might be some very cool interop at the very least.&lt;/p&gt;
&lt;p&gt;Apart from simulations, I&amp;rsquo;d like to mention these two talks in particular.
Real science meets Blender. The kicker is that we are helping science along just by interacting with Blender!
Apart from that, this demonstrates that &lt;em&gt;large&lt;/em&gt; numbers of MPM particles will work just fine.&lt;/p&gt;
&lt;div style=&#34;display: flex; justify-content: center; gap: 10px&#34;&gt;
  &lt;iframe width=&#34;420&#34; height=&#34;315&#34; allowfullscreen
    src=&#34;https://www.youtube.com/embed/WPajSWX730o&#34;&gt;
  &lt;/iframe&gt;
  &lt;iframe width=&#34;420&#34; height=&#34;315&#34; allowfullscreen
    src=&#34;https://www.youtube.com/embed/Krr89_b0QAI&#34;&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;And one encounter was especially inspiring: William the Wunderkind.
He created a full-blown piano with keys, hammers, dampers, and more, all in Blender, all by himself.
Not something you would expect from the average child!
The piano isn&amp;rsquo;t just for static renders, no.
Using the knife tool and Blender&amp;rsquo;s native physics, he created an eye-catching animation: the piano goes into an industrial shredder.
Amazing!
We got to talk about Squishy Volumes, and I don&amp;rsquo;t think anyone at the conference asked questions about the extension with such genuine interest.
The piano-into-the-shredder-scene is a really tough challenge for Squishy Volumes, and, of course, now it is a mandatory Milestone to reach.
Thank you for this inspiration, William.&lt;/p&gt;
&lt;p&gt;Another thing that cannot be overstated is that &lt;a href=&#34;https://www.imdb.com/title/tt4772188/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Flow&lt;/a&gt;, and Blender by extension, won the Oscar!
We got to pose with it, how cool is that?!
Here are two pictures, and BTW all these people are actually members of the aforementioned Discord server.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&#34;https://algebraic.games/blog/bcon_2025/20250918_BCON_Foto_Jelmer_de_HaasPOR_7208.jpg&#34;/&gt;
  &lt;img src=&#34;https://algebraic.games/blog/bcon_2025/20250918_BCON_Foto_Jelmer_de_HaasPOR_6588.jpg&#34;/&gt;
  &lt;figcaption&gt;
    Photos by &lt;a href=&#34;https://jelmerdehaas.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Jelmer de Haas&lt;/a&gt;,
    &lt;a href=&#34;https://conference.blender.org/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Blender Conference 2025&lt;/a&gt;,
    licensed under &lt;a href=&#34;https://creativecommons.org/licenses/by/4.0/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;CC BY 4.0&lt;/a&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;After every conference day, and before the first one, there&amp;rsquo;s a semi-official meetup at this cozy bar: &lt;a href=&#34;https://debalie.nl/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;deBALIE&lt;/a&gt;.
This is a very accessible way to continue that important discussion or to just relax and reminisce.&lt;/p&gt;
&lt;p&gt;As usual for me, two days after the conference, I&amp;rsquo;m in bed with a high fever.
It seems that it also got a good number of other attendees.
Another thing in common, I guess.&lt;/p&gt;
&lt;p&gt;I attended the conference with the hope of inspiring others.
I&amp;rsquo;m happy with my attempt and am eager to continue on this course.
I found myself returning with a head full of fresh ideas, new friends, and a glowing soul.
I can barely contain my excitement! ❤❤❤&lt;/p&gt;
&lt;p&gt;PS: if you haven&amp;rsquo;t, please try &lt;a href=&#34;https://algebraic.games/squishy_volumes/&#34;&gt;Squishy Volumes&lt;/a&gt; and join our journey!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>About Algebraic</title>
      <link>https://algebraic.games/blog/welcome/</link>
      <pubDate>Tue, 17 Jun 2025 00:00:00 +0000</pubDate>
      <guid>https://algebraic.games/blog/welcome/</guid>
      <description>&lt;p&gt;Welcome!&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m excited to introduce Algebraic, an independent company based in Germany, focused on developing plugins and games.&lt;/p&gt;
&lt;p&gt;A little about me: I founded Algebraic in late 2024 to pursue my goal of publishing a custom physics engine for Blender, offering specialized features, dedicated support, and remote compute services. With over five years of professional software development experience, a Master’s degree in Computer Science from RWTH Aachen, and a genuine passion for Blender, I&amp;rsquo;m committed to delivering high-quality tools for creators.&lt;/p&gt;
&lt;p&gt;Fueled by curiosity, coffee, and the occasional cat nap from my companions, Miri and Mia.
Thanks for visiting!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Rust Blender Extension API with Hot Reloading</title>
      <link>https://algebraic.games/blog/rust_extension_api/</link>
      <pubDate>Tue, 17 Jun 2025 00:00:00 +0000</pubDate>
      <guid>https://algebraic.games/blog/rust_extension_api/</guid>
      <description>&lt;p&gt;Blender and Rust are two of my favorite things. Over the years, I&amp;rsquo;ve tried a few ways of combining them, and here I&amp;rsquo;d like us to retrace the steps that led to one way that I&amp;rsquo;m particularly fond of:&lt;/p&gt;
&lt;p&gt;A Blender extension that calls &lt;em&gt;reloadable&lt;/em&gt; Rust code. Find the project with build instructions on &lt;a href=&#34;https://github.com/Algebraic-UG/blend_rust&#34;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As an example, this extension takes a mesh object and creates a new object consisting of point samples that are inside the mesh.
To detect the inside, the extension leverages &lt;a href=&#34;https://igl.ethz.ch/projects/winding-number/&#34;&gt;generalized winding numbers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a screenshot of the &lt;a href=&#34;#see-it-in-action&#34;&gt;video at the end&lt;/a&gt;.
&lt;img src=&#34;https://algebraic.games/blog/rust_extension_api/intro.png&#34;/&gt;&lt;/p&gt;
&lt;p&gt;Note that you could achieve this entirely with Geometry Nodes in default Blender by chaining a &lt;a href=&#34;https://docs.blender.org/manual/en/3.3/modeling/geometry_nodes/mesh/mesh_to_volume.html&#34;&gt;Mesh to Volume Node&lt;/a&gt; with a &lt;a href=&#34;https://docs.blender.org/manual/en/latest/modeling/geometry_nodes/point/distribute_points_in_volume.html&#34;&gt;Distribute Points in Volume&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;motivation&#34;&gt;Motivation&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s assume the following. We have our idea for Blender and want to build the core logic with Rust. We prefer Rust over Python, maybe because of performance, or because we want to reuse some other Rust code, or maybe just because we think that Rust strikes the best compromises of any programming language out there!&lt;/p&gt;
&lt;p&gt;We don&amp;rsquo;t want to create a custom Blender build from source (in this case, C/C++ would probably be the better choice), and we expect to tinker in the core (Rust) implementation for the most part of the development. Also, and this is key, the application programming interface (API) &lt;em&gt;is not going to change much&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In essence, we want to use Rust and have fast development cycles.&lt;/p&gt;
&lt;h2 id=&#34;making-choices&#34;&gt;Making Choices&lt;/h2&gt;
&lt;p&gt;There are many ways to get this done. I&amp;rsquo;d like to mention one important alternative right now: (re-)start a separate local server process.
If your API is going to change a lot, you might want to look into that instead.&lt;/p&gt;
&lt;h3 id=&#34;blenders-extensions&#34;&gt;Blender&amp;rsquo;s Extensions&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s make it our first goal to avoid rebuilding Blender from source. A good way to achieve this is to build an &lt;a href=&#34;https://docs.blender.org/manual/en/latest/advanced/extensions/&#34;&gt;extension&lt;/a&gt;. Extensions are easy to distribute and install. Extensions are written in Python, and we get an extensive API with &lt;a href=&#34;https://pypi.org/project/bpy/&#34;&gt;Blender&amp;rsquo;s Python Module (bpy)&lt;/a&gt;. We can write up our extension and bundle it into a ZIP file with Blender (from the command line). This ZIP file can then be installed from within Blender.&lt;/p&gt;
&lt;p&gt;Within the Python part, we need to do three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Register and build the user interface&lt;/li&gt;
&lt;li&gt;Read and write Blender&amp;rsquo;s Data&lt;/li&gt;
&lt;li&gt;Call our core logic&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Python is good, but we want Rust for the core logic!&lt;/p&gt;
&lt;h3 id=&#34;wheels-make-it-go&#34;&gt;Wheels Make it Go!&lt;/h3&gt;
&lt;p&gt;So, there&amp;rsquo;s our next challenge: how can we bridge between our beloved Rust and Python, which seems to be a mandatory stopover. Since both languages have been very popular for years, it&amp;rsquo;s unsurprising that a mature solution exists: &lt;a href=&#34;https://github.com/PyO3/pyo3&#34;&gt;PyO3&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It seems natural to wrap our core logic into a Python package, a &lt;a href=&#34;https://pythonwheels.com/&#34;&gt;wheel&lt;/a&gt;. This allows us to import it in other Python code for automatic testing. More importantly, this is the &lt;a href=&#34;https://docs.blender.org/manual/en/latest/advanced/extensions/python_wheels.html&#34;&gt;default way for Blender extensions&lt;/a&gt; to load binary code. PyO3&amp;rsquo;s &lt;a href=&#34;https://github.com/PyO3/maturin&#34;&gt;maturin&lt;/a&gt; will turn our Rust code into a wheel file.&lt;/p&gt;
&lt;p&gt;This file needs to go into our extension&amp;rsquo;s source tree, and we need to list it in the extension&amp;rsquo;s manifest; it&amp;rsquo;ll be bundled with the ZIP file, and we&amp;rsquo;ll be able to import our module from the extension code!&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s visualize where we are at.&lt;/p&gt;
&lt;svg height=&#34;10&#34;&gt;
  &lt;img src=&#34;https://algebraic.games/blog/rust_extension_api/call_rust_from_blender.svg&#34;/&gt;
&lt;/svg&gt;
&lt;p&gt;Great, right? Nope, it&amp;rsquo;s horrible!&lt;/p&gt;



  
  &lt;blockquote class=&#34;border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6&#34;&gt;
    &lt;p&gt;we expect to tinker in the core implementation for the most part of the development&lt;/p&gt;

  &lt;/blockquote&gt;

&lt;p&gt;The issue is that this approach doesn&amp;rsquo;t allow us to iterate on changes in the core logic with any speed to speak of. And it might be worse than you think.&lt;/p&gt;
&lt;h3 id=&#34;dynamic-libraries-are-too-static&#34;&gt;Dynamic Libraries are too Static&lt;/h3&gt;
&lt;p&gt;First, let&amp;rsquo;s go back to how we &amp;ldquo;magically&amp;rdquo; get to call our Rust code from Python. A crucial detail for the wrapping library crate is that it needs to have the crate type &lt;a href=&#34;https://doc.rust-lang.org/reference/linkage.html#r-link.cdylib&#34;&gt;cdylib&lt;/a&gt;, meaning it&amp;rsquo;s going to create a &lt;a href=&#34;https://en.wikipedia.org/wiki/Shared_library&#34;&gt;shared library&lt;/a&gt; with the binary code compiled from our Rust code. Then, the Python side can request that this shared library be loaded by the operating system (OS), find functions by name (there&amp;rsquo;s a lookup table), and call them. That is, assuming that the &lt;a href=&#34;https://en.wikipedia.org/wiki/Application_binary_interface&#34;&gt;application binary inferface&lt;/a&gt; (arguments, etc.) matches what the Python side expects.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Dynamic&lt;/em&gt; and &lt;em&gt;shared&lt;/em&gt; libraries refer to the same thing, but the OS prefers to think about them as &lt;em&gt;shared&lt;/em&gt;. The OS would rather have different calls, threads, and processes reuse and &lt;em&gt;share&lt;/em&gt; the library as much as possible. This is nice, but it hinders us from swapping it out for a new version! While it is possible to unload a shared library in theory, it isn&amp;rsquo;t easy to do reliably in practice. For example, the &lt;a href=&#34;https://linux.die.net/man/3/dlclose&#34;&gt;dlclose&lt;/a&gt; function Linux provides won&amp;rsquo;t work if any of the library&amp;rsquo;s symbols are still &amp;ldquo;in use&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;We finally arrive at the issue: we &lt;strong&gt;cannot change anything&lt;/strong&gt; in our core logic and use it in our &lt;strong&gt;running Blender process&lt;/strong&gt;. Instead, we have to shut down Blender, rebuild everything, and start again. Only to find that some cache still has our old version! Explicitly deactivate our extension, rinse, repeat.&lt;/p&gt;
&lt;p&gt;This is annoying, especially if you have to reproduce some state in Blender to test a particular thing.&lt;/p&gt;
&lt;h3 id=&#34;finally-reloadable-code&#34;&gt;Finally, Reloadable Code&lt;/h3&gt;
&lt;p&gt;Actually, for the Python side, hot-reloading is readily available. We can use VSCode with this &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=JacquesLucke.blender-development&#34;&gt;super helpful addon&lt;/a&gt; for extension development. Not only does this alleviate the need for explicitly building the ZIP with Blender on the command line (and installing it from disk), but it also gives us the option to reload our Python code!&lt;/p&gt;
&lt;p&gt;So, that is the Python side done.&lt;/p&gt;
&lt;p&gt;For the Rust side, we can go with David Wheeler: &amp;ldquo;All problems in computer science can be solved by another level of indirection.&amp;rdquo; There is a &lt;a href=&#34;https://robert.kra.hn/posts/hot-reloading-rust/&#34;&gt;hot-lib-reloader&lt;/a&gt; solution in pure Rust that we can use between our core logic and our wrapping crate!&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not entirely sure how it works, but I believe the rather cheeky trick is to constantly make new names for the shared library that we want to be reloadable. This way, the operating system is forced to load the latest version from disk. Of course, the upper layers of our Matryoshka extensions are none the wiser, which is good!&lt;/p&gt;
&lt;p&gt;So, that is the Rust side done, as well!&lt;/p&gt;
&lt;h2 id=&#34;live-with-the-choices&#34;&gt;Live With the Choices&lt;/h2&gt;
&lt;div class=&#34;tenor-gif-embed&#34; data-postid=&#34;18470751&#34; data-share-method=&#34;host&#34; data-aspect-ratio=&#34;1.77778&#34; data-width=&#34;100%&#34;&gt;&lt;a href=&#34;https://tenor.com/view/good-idea-i-stand-by-it-itysl-i-think-you-should-leave-gif-18470751&#34;&gt;Good Idea I Stand By It GIF&lt;/a&gt;from &lt;a href=&#34;https://tenor.com/search/good+idea-gifs&#34;&gt;Good Idea GIFs&lt;/a&gt;&lt;/div&gt; &lt;script type=&#34;text/javascript&#34; async src=&#34;https://tenor.com/embed.js&#34;&gt;&lt;/script&gt;
&lt;h3 id=&#34;preventing-race-conditions&#34;&gt;Preventing Race Conditions&lt;/h3&gt;
&lt;p&gt;Actually, we&amp;rsquo;re not quite done with the Rust side. With our trickery, we&amp;rsquo;re angering the race-condition-gods, and we need to place at least one synchronization primitive to appease them.&lt;/p&gt;
&lt;p&gt;Unfortunately, there are several subtle ways to mess this up because there is a thread from the hot-lib-reloader whose task is to replace our library, and it acts asynchronously. Here are some ideas of what might go wrong:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Some of the binary code we&amp;rsquo;re replacing is currently running&lt;/li&gt;
&lt;li&gt;We have handed references to some structs in Rust to Python, and now their layout has changed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And there&amp;rsquo;s more: even though Python would traditionally allow only one call to our module at a time, due to its global interpreter lock (GIL), that &lt;a href=&#34;https://peps.python.org/pep-0703/&#34;&gt;isn&amp;rsquo;t necessarily the case anymore&lt;/a&gt;. And in our case, we&amp;rsquo;re going to get parallel calls from Blender and its render engine.&lt;/p&gt;
&lt;p&gt;For once, we&amp;rsquo;re happy about the persistence of the shared libraries. It&amp;rsquo;s not possible to pull the shared library from under our feet while we&amp;rsquo;re still using it!&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re not going to deal with all issues in a foolproof way, but the following tricks get us there most of the way.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use a &amp;ldquo;context&amp;rdquo; trait to expose core logic&lt;/li&gt;
&lt;li&gt;Allow a single context instance to exist at one time&lt;/li&gt;
&lt;li&gt;Protect this instance with a mutex&lt;/li&gt;
&lt;li&gt;Explicitly synchronize the library-reloading-thread&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We think about this context as our reloadable library. Now we&amp;rsquo;re jumping ahead to show a few relevant places in the code: we&amp;rsquo;re left with a single function that needs to be hot-reloaded:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#[unsafe(no_mangle)]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;pub&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;create_context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-&amp;gt; &lt;span class=&#34;nb&#34;&gt;Box&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;dyn&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Context&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;fm&#34;&gt;println!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;creating new rust context&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Box&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Impl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the spicy part of the code that deals with reloading looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lib_observer&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rust_hot_reload&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;loop&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// wait for reload and block it
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;update_blocker&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lib_observer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;wait_for_about_to_reload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// wait for any library calls to finish and block further calls
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context_guard&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;no&#34;&gt;LOCK&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unwrap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// cleanup
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context_guard&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;take&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// this should block until any threads are joined and any resources are dropped
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;drop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// let the library update commence and wait until it&amp;#39;s finished
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;drop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;update_blocker&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lib_observer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;wait_for_reload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// fresh context with new lib version
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context_guard&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rust_hot_reload&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;create_context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// context_guard is dropped and calls can continue
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The middle part here is where we can still mess up. If we have any threads or resources in our core logic that might cause trouble when it&amp;rsquo;s replaced, they need to be joined and cleaned up when the context is dopped in a &lt;em&gt;blocking fashion&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&#34;the-catch&#34;&gt;The Catch&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ve hinted at this a few times; now it&amp;rsquo;s time to stop beating around the bush. Even with our fancy-schmancy reloading schemes, &lt;strong&gt;we should not hot-reload if we change our API&lt;/strong&gt; (aka. context trait). If we do this anyway, all bets are off, and undefined behavior (UB) ensues. Let me point to the &lt;a href=&#34;https://robert.kra.hn/posts/hot-reloading-rust/#caveats-and-asterisks&#34;&gt;hot-lib-reloader&lt;/a&gt; blog post again for further caveats. Whether this partial reloadability is worth giving up certain safety guarantees &lt;strong&gt;during development&lt;/strong&gt; is for you to decide.&lt;/p&gt;
&lt;p&gt;Of course, we can regain the safety we&amp;rsquo;re losing as soon as we compile without hot reloading. Once we&amp;rsquo;re building to distribute, &lt;a href=&#34;https://github.com/Algebraic-UG/blend_rust?tab=readme-ov-file#building-without-hot-reloading &#34;&gt;we can&amp;rsquo;t use hot reloading anyway&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;overview-of-the-code&#34;&gt;Overview of the Code&lt;/h2&gt;
&lt;p&gt;Here, we examine the top-level structure, summarize the build and reload process, and conclude with testing the hot reloading.&lt;/p&gt;
&lt;p&gt;We have four different Rust directories (crates) serving various purposes and a single python directory.
Let&amp;rsquo;s visualize this!&lt;/p&gt;
&lt;svg height=&#34;20&#34;&gt;
  &lt;img src=&#34;https://algebraic.games/blog/rust_extension_api/full_picture.svg&#34;/&gt;
&lt;/svg&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;tt&gt;rust_api&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;This crate defines context trait; it&amp;rsquo;s not reloadable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;tt&gt;rust_core&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;This crate implements the context trait; we implement the core logic here, and it&amp;rsquo;s reloadable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;tt&gt;rust_hot&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;This crate exposes one reloadable function that creates an instance of the context (and re-exports the context trait). We don&amp;rsquo;t need to edit this; it only exists to facilitate splitting the API and core logic. This crate is also the one we&amp;rsquo;ll rebuild for reloading.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;tt&gt;rust_wrap&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;This crate is on the boundary to Python. Here, we rely on PyO3 to create a Python module and also utilize the hot-lib-reloader to orchestrate the reloading of the context creation function.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For the first build and, whenever we change something in the API, we let maturin create a new wheel from rust_wrap and build rust_hot using the standard cargo command. If Blender is running a prior version of our extension, disable the extension and shut down the Blender process as well.&lt;/p&gt;
&lt;p&gt;However, for any subsequent changes to our rust_core, we only need to rebuild rust_hot using the standard cargo command.&lt;/p&gt;
&lt;h3 id=&#34;see-it-in-action&#34;&gt;See it in Action&lt;/h3&gt;
&lt;p&gt;Now we can tinker in our core logic to our heart&amp;rsquo;s content and immediately see the result in Blender. Check this out:&lt;/p&gt;
 &lt;iframe width=&#34;420&#34; height=&#34;315&#34; allowfullscreen
  src=&#34;https://www.youtube.com/embed/PD6Cn409GUM&#34;&gt;
&lt;/iframe&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;First, let&amp;rsquo;s address the drawbacks: this approach is clunky to get started, and it doesn&amp;rsquo;t alleviate the growing pains of the API. If we reload with a changed API, we&amp;rsquo;re toast (we get UB). And if we&amp;rsquo;re not careful in cleaning up our context, we might get bitten by zombie threads.&lt;/p&gt;
&lt;p&gt;But once it&amp;rsquo;s up and running, we can write Rust code as if it were a natively supported language! We have direct function calls without any serialization overhead and benefit from the Rust ecosystem to accomplish nearly everything we want, from high-performance numeric parallel GPU tasks to complex networking.&lt;/p&gt;
&lt;p&gt;Furthermore, we could build on existing solutions that work and primarily focus on plumbing. Shoutout to PyO3, the Blender Development addon, hot-lib-reloader, and, of course, Blender, Python, and Rust! ❤❤❤&lt;/p&gt;
&lt;p&gt;Thank you for reading!&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
