<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>ShapeReality - Blog - Posts</title>
    <link rel="self" type="application/atom+xml" href="https://blog.shapereality.io/posts/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://blog.shapereality.io/posts/"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2025-08-18T00:00:00+00:00</updated>
    <id>https://blog.shapereality.io/posts/atom.xml</id>
    <entry xml:lang="en">
        <title>Voxels 👾</title>
        <published>2025-08-18T00:00:00+00:00</published>
        <updated>2025-08-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.shapereality.io/posts/voxels/"/>
        <id>https://blog.shapereality.io/posts/voxels/</id>
        
        <content type="html" xml:base="https://blog.shapereality.io/posts/voxels/">&lt;p&gt;This is going to be a brief post, but with an image that makes me exceptionally happy:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;voxels.jpg&quot; alt=&quot;Voxels&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve been implementing a voxel data structure similar to an octree, but with a larger branching factor (still powers of 2), that contains a hashmap at the root of the tree. It takes inspiration from the paper &lt;a href=&quot;https:&#x2F;&#x2F;www.museth.org&#x2F;Ken&#x2F;Publications_files&#x2F;Museth_TOG13.pdf&quot;&gt;&quot;VDB: High-Resolution Sparse Volumes with Dynamic Topology&quot; by Ken Museth&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The image shows the mesh graph voxelized into a tree containing depths 2, 3 and 4 (&lt;code&gt;VDB&amp;lt;2, 3, 4&amp;gt;&lt;&#x2F;code&gt;). Depth 2 gives 64 children, 3 gives 512 children, and 4 gives 4096 children. See the following overview:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Depth&lt;&#x2F;th&gt;&lt;th&gt;Child count&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;8&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;td&gt;64&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;&#x2F;td&gt;&lt;td&gt;512&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;4&lt;&#x2F;td&gt;&lt;td&gt;4096&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;5&lt;&#x2F;td&gt;&lt;td&gt;32768&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Having these higher child counts (i.e. branching factor), helps to keep the tree very wide, as opposed to an octree, where each depth only has 8 children.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Simple undo history for a mesh graph using diffs</title>
        <published>2025-08-14T00:00:00+00:00</published>
        <updated>2025-08-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.shapereality.io/posts/undo/"/>
        <id>https://blog.shapereality.io/posts/undo/</id>
        
        <content type="html" xml:base="https://blog.shapereality.io/posts/undo/">&lt;p&gt;Undo is a fundamental feature in many software applications, and my app ShapeReality - 3D Modeling is no exception. Due to the ubiquity of undo, one might expect implementing it to be a solved problem, but there commonly isn&#x27;t a one-size-fits-all approach.&lt;&#x2F;p&gt;
&lt;p&gt;This post will go into how I implemented undo for my mesh graph data structure, and how this fits into the complete undo history of an application containing multiple data structures and contexts. These principles should be applicable to other applications and data structures too.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;command-pattern&quot;&gt;Command pattern&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#command-pattern&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;At the core of implementing an undo history is the &lt;em&gt;command pattern&lt;&#x2F;em&gt;. Each operation that should be reversable via &lt;code&gt;undo&lt;&#x2F;code&gt;, and executed again via &lt;code&gt;redo&lt;&#x2F;code&gt;, is stored in an &lt;code&gt;ICommand&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#282a36;color:#f8f8f2;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;ICommand &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;virtual &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;undo&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bd93f9;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;virtual &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;redo&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bd93f9;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;undo-and-redo-stack&quot;&gt;Undo and redo stack&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#undo-and-redo-stack&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Commands get stored in an &lt;code&gt;undo_stack&lt;&#x2F;code&gt; and &lt;code&gt;redo_stack&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#282a36;color:#f8f8f2;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;History&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  std&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;stack&amp;lt;ICommand&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; undo_stack;
&lt;&#x2F;span&gt;&lt;span&gt;  std&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;stack&amp;lt;ICommand&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; redo_stack;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Any time a command is performed, it gets pushed onto the &lt;code&gt;undo_stack&lt;&#x2F;code&gt;, and the &lt;code&gt;redo_stack&lt;&#x2F;code&gt; gets cleared.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Alternatively, one could create branching undo, where each time you undo, and then perform new changes, it creates a new branch. While this could be an interesting feature, it requires a lot of additional UI to communicate the existence and state of this branching history. Compared to two simple buttons, this complexity doesn&#x27;t weigh up against the benefits.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;When &lt;code&gt;History::undo()&lt;&#x2F;code&gt; is called, it takes the top of the &lt;code&gt;undo_stack&lt;&#x2F;code&gt;, calls &lt;code&gt;Command::undo()&lt;&#x2F;code&gt; on it, and pushes it onto the &lt;code&gt;redo_stack&lt;&#x2F;code&gt;. The inverse is done for &lt;code&gt;History::redo()&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;note-about-invalid-states&quot;&gt;Note about invalid states&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#note-about-invalid-states&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;When the user performs any modification to the data structure, it &lt;em&gt;must&lt;&#x2F;em&gt; be stored as a command. If a change is not recorded, and &lt;code&gt;History::undo()&lt;&#x2F;code&gt; is called afterwards, the data structure can end up in an invalid state. See the following simplified example:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;State before&lt;&#x2F;th&gt;&lt;th&gt;Operation&lt;&#x2F;th&gt;&lt;th&gt;State after&lt;&#x2F;th&gt;&lt;th&gt;Undo stack after operation&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;ABCDEF&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;erase(1), store&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;ACDEF&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;[EraseCommand(1, &#x27;B&#x27;)]&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;ACDEF&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;erase(0), don&#x27;t store&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;CDEF&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;[EraseCommand(1, &#x27;B&#x27;)]&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;CDEF&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;undo EraseCommand(1, &#x27;B&#x27;)&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;CBDEF&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;[]&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;If the letters are required to be in alphabetical order, the non-recorded operation caused the &lt;code&gt;undo&lt;&#x2F;code&gt; operation to make the state invalid.&lt;&#x2F;p&gt;
&lt;p&gt;The mesh graph also has such requirements, for example:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;When two vertices are selected that connect to each other via an edge, the edge must also be selected&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;When all vertices &#x2F; edges are selected that make up a face, the face must also be selected&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For this reason, it&#x27;s important that anything modifying the mesh graph is pushed to the history as an undoable command.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;command-implementation-for-mesh-graph-the-dumb-approach&quot;&gt;Command implementation for mesh graph - the dumb approach&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#command-implementation-for-mesh-graph-the-dumb-approach&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;There are many operations that can be performed on the mesh graph. Take for example:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#282a36;color:#f8f8f2;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Mesh&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6272a4;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;insert_vertex&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;insert_edge&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;insert_face&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;erase_vertex&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;erase_edge&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;erase_face&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;insert_face_or_edge_from_selected&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;extrude_selected&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;select_linked&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6272a4;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To make these operations undoable, I considered a naive approach, where a corresponding command has to be created for each operation. e.g.:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#282a36;color:#f8f8f2;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;InsertVertexCommand : &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;font-style:italic;color:#8be9fd;&quot;&gt;ICommand &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6272a4;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;undo&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;override&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;redo&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;override&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;InsertEdgeCommand : &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;font-style:italic;color:#8be9fd;&quot;&gt;ICommand &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6272a4;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;undo&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;override&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;redo&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;override&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6272a4;&quot;&gt;&#x2F;&#x2F; etc.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;However, life&#x27;s too short for this. Not only does this make the code needlessly verbose, it&#x27;s also a pain to figure out what data to store in the command and how to reverse the operation. Each function can also call the other functions recursively, thus making this approach highly complex to implement.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-super-lazy-approach-pool-copies&quot;&gt;The super lazy approach - pool copies&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#the-super-lazy-approach-pool-copies&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Recall that the mesh graph contains &lt;a href=&quot;&#x2F;posts&#x2F;editable-mesh&#x2F;&quot;&gt;5 elements&lt;&#x2F;a&gt; (&lt;code&gt;Vertex&lt;&#x2F;code&gt;, &lt;code&gt;Edge&lt;&#x2F;code&gt;, &lt;code&gt;Face&lt;&#x2F;code&gt;, &lt;code&gt;VertexUse&lt;&#x2F;code&gt; and &lt;code&gt;EdgeUse&lt;&#x2F;code&gt;). These elements are stored in a pool.&lt;&#x2F;p&gt;
&lt;p&gt;If we&#x27;re exceptionally lazy, we can copy the pools before and after executing the operation, and on undo() assign the old pools to the pools, and on redo assign the new pools to the pools.&lt;&#x2F;p&gt;
&lt;p&gt;The issue with this however, is that it takes up an insane amount of memory. For each operation performed, no matter how small, we store an entire copy of the mesh in working memory. For sufficiently large meshes, this approach won&#x27;t work.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-less-lazy-approach-pool-diffs&quot;&gt;The less lazy approach - pool diffs&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#the-less-lazy-approach-pool-diffs&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;This brings me to the final design:&lt;&#x2F;p&gt;
&lt;p&gt;Instead of storing a copy of the entire pool in a command, we only store elements that were changed. Elements are considered changed if they: 1) were deleted, 2) were inserted or 3) had their data changed.&lt;&#x2F;p&gt;
&lt;p&gt;For each changed element, we store the before and after state. This gets stored in a &lt;code&gt;PoolDiff&amp;lt;T&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#282a36;color:#f8f8f2;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;template &lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;typename&lt;&#x2F;span&gt;&lt;span&gt; T&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;PoolDiff &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Entry&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;unsigned int&lt;&#x2F;span&gt;&lt;span&gt; index;
&lt;&#x2F;span&gt;&lt;span&gt;        T before;
&lt;&#x2F;span&gt;&lt;span&gt;        T after;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;    std&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;vector&amp;lt;Entry&amp;gt; entries;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;undo&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50fa7b;&quot;&gt;redo&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;recording-a-diff&quot;&gt;Recording a diff&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#recording-a-diff&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Recording a diff is left as an exercise to the reader.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#conclusion&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;This post outlines how to implement undo &#x2F; redo for a more complex data structure, where it is borderline insane to create a separate &lt;code&gt;Command&lt;&#x2F;code&gt; subclass for each operation. Diffs can be a powerful approach, potentially applicable to a wide range of other applications.&lt;&#x2F;p&gt;
&lt;p&gt;Note that specific optimisations can still be done where the mesh does not change topologically.&lt;&#x2F;p&gt;
&lt;div class=&quot;app-download-card&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;apps.apple.com&#x2F;app&#x2F;shapereality-3d-modeling&#x2F;id6746662557&quot; target=&quot;_blank&quot;&gt;
        &lt;div class=&quot;app-card-content&quot;&gt;
            &lt;img src=&quot;&#x2F;images&#x2F;AppIcon.svg&quot; alt=&quot;ShapeReality App Icon&quot; class=&quot;app-icon&quot; &#x2F;&gt;
            &lt;div class=&quot;app-card-text&quot;&gt;
                &lt;p class=&quot;app-name&quot;&gt;ShapeReality - 3D Modeling&lt;&#x2F;p&gt;
                &lt;p class=&quot;app-description&quot;&gt;ShapeReality - 3D Modeling brings 3D polygon mesh modeling tools to your iPhone and iPad. Inspect, edit, save and share .obj files.&lt;&#x2F;p&gt;
            &lt;&#x2F;div&gt;
            &lt;img src=&quot;&#x2F;images&#x2F;download_appstore.svg&quot; alt=&quot;Download on the App Store&quot; class=&quot;appstore-badge&quot; &#x2F;&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;style&gt;
.app-download-card {
    width: 100%;
    background-color: #fff;
    border-radius: 16px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);
    margin: 0 auto;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
    border-color:#666;
    border-width:2px;
    border-style:dotted;
    padding: 1rem;
}

.app-card-content {
    display: flex;
    align-items: top;
    gap: 20px;
    flex-wrap: wrap;
}

.app-icon {
    width: 80px;
    height: 80px;
    flex-shrink: 0;
    margin: 0 !important;
}

.app-card-text {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-width: 0;
    flex: 1;
}

.app-name {
    font-size: 20px;
    font-weight: 600;
    margin: 0 0 2px !important;
    color: #111;
    line-height: 1.2;
}

.app-description {
    font-size: 14px;
    color: #666;
    margin: 0 0 12px !important;
}

.appstore-badge {
    width: 140px;
    height: auto;
}

@media (max-width: 600px) {
    .app-card-content {
        flex-direction: column;
        align-items: center;
        text-align: center;
    }

    .app-card-text {
        align-items: center;
    }
}
&lt;&#x2F;style&gt;</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Gizmos and iPad support</title>
        <published>2025-08-11T00:00:00+00:00</published>
        <updated>2025-08-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.shapereality.io/posts/gizmos-and-ipad/"/>
        <id>https://blog.shapereality.io/posts/gizmos-and-ipad/</id>
        
        <content type="html" xml:base="https://blog.shapereality.io/posts/gizmos-and-ipad/">&lt;h2 id=&quot;gizmos&quot;&gt;Gizmos&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#gizmos&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Translate, rotate and scale gizmos are now added to the app, which should make transforming elements a bit easier compared to manually dragging each element freely.&lt;&#x2F;p&gt;
&lt;p&gt;The translate, rotate and scale gizmo respectively:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;gizmos.jpg&quot; alt=&quot;Translate, Rotate and Scale gizmos&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The translate gizmo allows translating elements along an axis or a plane.&lt;&#x2F;p&gt;
&lt;p&gt;The rotate gizmo allows rotating elements around a specific axis.&lt;&#x2F;p&gt;
&lt;p&gt;And the scale gizmo allows scaling elements along an axis or a plane.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ipad-support&quot;&gt;iPad support&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#ipad-support&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;iPad and iPad Pro are now supported, and the user interface has been updated to accommodate the larger screen size.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;download-now&quot;&gt;Download now&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#download-now&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;div class=&quot;app-download-card&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;apps.apple.com&#x2F;app&#x2F;shapereality-3d-modeling&#x2F;id6746662557&quot; target=&quot;_blank&quot;&gt;
        &lt;div class=&quot;app-card-content&quot;&gt;
            &lt;img src=&quot;&#x2F;images&#x2F;AppIcon.svg&quot; alt=&quot;ShapeReality App Icon&quot; class=&quot;app-icon&quot; &#x2F;&gt;
            &lt;div class=&quot;app-card-text&quot;&gt;
                &lt;p class=&quot;app-name&quot;&gt;ShapeReality - 3D Modeling&lt;&#x2F;p&gt;
                &lt;p class=&quot;app-description&quot;&gt;ShapeReality - 3D Modeling brings 3D polygon mesh modeling tools to your iPhone and iPad. Inspect, edit, save and share .obj files.&lt;&#x2F;p&gt;
            &lt;&#x2F;div&gt;
            &lt;img src=&quot;&#x2F;images&#x2F;download_appstore.svg&quot; alt=&quot;Download on the App Store&quot; class=&quot;appstore-badge&quot; &#x2F;&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;style&gt;
.app-download-card {
    width: 100%;
    background-color: #fff;
    border-radius: 16px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);
    margin: 0 auto;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
    border-color:#666;
    border-width:2px;
    border-style:dotted;
    padding: 1rem;
}

.app-card-content {
    display: flex;
    align-items: top;
    gap: 20px;
    flex-wrap: wrap;
}

.app-icon {
    width: 80px;
    height: 80px;
    flex-shrink: 0;
    margin: 0 !important;
}

.app-card-text {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-width: 0;
    flex: 1;
}

.app-name {
    font-size: 20px;
    font-weight: 600;
    margin: 0 0 2px !important;
    color: #111;
    line-height: 1.2;
}

.app-description {
    font-size: 14px;
    color: #666;
    margin: 0 0 12px !important;
}

.appstore-badge {
    width: 140px;
    height: auto;
}

@media (max-width: 600px) {
    .app-card-content {
        flex-direction: column;
        align-items: center;
        text-align: center;
    }

    .app-card-text {
        align-items: center;
    }
}
&lt;&#x2F;style&gt;</content>
        
    </entry>
    <entry xml:lang="en">
        <title>An editable non-manifold polygon mesh data structure</title>
        <published>2025-07-28T00:00:00+00:00</published>
        <updated>2025-07-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.shapereality.io/posts/editable-mesh/"/>
        <id>https://blog.shapereality.io/posts/editable-mesh/</id>
        
        <content type="html" xml:base="https://blog.shapereality.io/posts/editable-mesh/">&lt;p&gt;One core data structure in the ShapeReality - 3D Modeling application is a non-manifold polygon mesh data structure. This data structure is a graph of &lt;code&gt;Vertices&lt;&#x2F;code&gt;, &lt;code&gt;Edges&lt;&#x2F;code&gt; and &lt;code&gt;Faces&lt;&#x2F;code&gt;. It takes inspiration from &lt;a href=&quot;https:&#x2F;&#x2F;developer.blender.org&#x2F;docs&#x2F;features&#x2F;objects&#x2F;mesh&#x2F;bmesh&#x2F;&quot;&gt;Blender&#x27;s BMesh data structure&lt;&#x2F;a&gt;, the Radial Edge Structure (see &lt;a href=&quot;https:&#x2F;&#x2F;docs.nvidia.com&#x2F;smlib&#x2F;manual&#x2F;smlib&#x2F;topology&#x2F;index.html&quot;&gt;Nvidia SMLib - Topology documentation&lt;&#x2F;a&gt;) and the paper &lt;a href=&quot;https:&#x2F;&#x2F;asmedigitalcollection.asme.org&#x2F;computingengineering&#x2F;article-abstract&#x2F;1&#x2F;4&#x2F;356&#x2F;471622&#x2F;Partial-Entity-Structure-A-Compact-Boundary?redirectedFrom=fulltext&quot;&gt;Partial Entity Structure: A Compact Boundary Representation for Non-Manifold Geometric Modeling by Lee et al.&lt;&#x2F;a&gt;, but arrives at a simpler design that still has the desired feature set.&lt;&#x2F;p&gt;
&lt;p&gt;The data structure supports:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;non-manifold edges, meaning more than 2 faces can be connected to a given edge&lt;&#x2F;li&gt;
&lt;li&gt;loose vertices (vertices with no edges connected to them)&lt;&#x2F;li&gt;
&lt;li&gt;loose edges (edges with no faces connected to them)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The main goal of the data structure is to enable adjacency &#x2F; topological queries:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;For a given vertex, which edges are connected to it?&lt;&#x2F;li&gt;
&lt;li&gt;For a given edge, which faces are connected to it?&lt;&#x2F;li&gt;
&lt;li&gt;For a given face, which vertices &#x2F; edges make up the face?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;mesh.jpg&quot; alt=&quot;Mesh&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;vertex-uses&quot;&gt;Vertex uses&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#vertex-uses&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;We wish to have fast insertion and deletion of elements, and no memory fragmentation.
Therefore, a naive implementation such as:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#282a36;color:#f8f8f2;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Vertex &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  std&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;vector&amp;lt;Edge&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; connected_edges;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;would not be a good idea.&lt;&#x2F;p&gt;
&lt;p&gt;Therefore, for a given &lt;code&gt;Vertex&lt;&#x2F;code&gt;, we add a circularly linked list of &lt;code&gt;VertexUses&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;VertexUses.svg&quot; alt=&quot;VertexUses&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#282a36;color:#f8f8f2;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#6272a4;&quot;&gt;&#x2F;&#x2F; circularly linked list
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;VertexUse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  Vertex&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; vertex;
&lt;&#x2F;span&gt;&lt;span&gt;  VertexUse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; prev;
&lt;&#x2F;span&gt;&lt;span&gt;  VertexUse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; next;
&lt;&#x2F;span&gt;&lt;span&gt;  Edge&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; connected_edge;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Vertex &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  VertexUse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; uses;
&lt;&#x2F;span&gt;&lt;span&gt;  Point&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; point;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This way, we can iterate over all &lt;code&gt;Edges&lt;&#x2F;code&gt; of a given &lt;code&gt;Vertex&lt;&#x2F;code&gt;, and not have millions of separate instances of &lt;code&gt;std::vector&amp;lt;Edge*&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;edge-uses&quot;&gt;Edge uses&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#edge-uses&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;This same approach is then also used for &lt;code&gt;Edges&lt;&#x2F;code&gt;, which get an accompanying circularly linked list of &lt;code&gt;EdgeUses&lt;&#x2F;code&gt; to answer the question &lt;em&gt;&quot;for a given edge, which faces are connected to it?&quot;&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;EdgeUses.svg&quot; alt=&quot;EdgeUses&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#282a36;color:#f8f8f2;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;EdgeUse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  Edge&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; edge;
&lt;&#x2F;span&gt;&lt;span&gt;  EdgeUse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; prev;
&lt;&#x2F;span&gt;&lt;span&gt;  EdgeUse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; next;
&lt;&#x2F;span&gt;&lt;span&gt;  Face&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; connected_face;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Edge &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  EdgeUse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; uses;
&lt;&#x2F;span&gt;&lt;span&gt;  VertexUse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; v0;
&lt;&#x2F;span&gt;&lt;span&gt;  VertexUse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; v1;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;face-loop&quot;&gt;Face loop&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#face-loop&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Then, the final task is to add the &lt;code&gt;FaceLoop&lt;&#x2F;code&gt;, which answers the question &lt;em&gt;&quot;for a given face, which vertices &#x2F; edges make up the given face?&quot;&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;Note that we also store the vertex for a given &lt;code&gt;FaceLoop&lt;&#x2F;code&gt; segment, because &lt;code&gt;v0&lt;&#x2F;code&gt; and &lt;code&gt;v1&lt;&#x2F;code&gt; of an &lt;code&gt;Edge&lt;&#x2F;code&gt; are unordered.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;FaceLoop.svg&quot; alt=&quot;FaceLoop&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#282a36;color:#f8f8f2;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#6272a4;&quot;&gt;&#x2F;&#x2F; circularly linked list
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;FaceLoop &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  Face&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; face;
&lt;&#x2F;span&gt;&lt;span&gt;  FaceLoop&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; prev;
&lt;&#x2F;span&gt;&lt;span&gt;  FaceLoop&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; next;
&lt;&#x2F;span&gt;&lt;span&gt;  EdgeUse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; edge;
&lt;&#x2F;span&gt;&lt;span&gt;  VertexUse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; vertex;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Face &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  FaceLoop&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; loop;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;merging-face-loop-and-edge-uses&quot;&gt;Merging face loop and edge uses&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#merging-face-loop-and-edge-uses&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Because a given &lt;code&gt;EdgeUse&lt;&#x2F;code&gt; is only attached to a single face, we can merge the &lt;code&gt;FaceLoop&lt;&#x2F;code&gt; linked list into the &lt;code&gt;EdgeUse&lt;&#x2F;code&gt;, thus reducing the need for an additional element type in the graph:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#282a36;color:#f8f8f2;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;EdgeUse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  Edge&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; edge;
&lt;&#x2F;span&gt;&lt;span&gt;  Face&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; attached_face;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6272a4;&quot;&gt;&#x2F;&#x2F; radial (circularly linked list)
&lt;&#x2F;span&gt;&lt;span&gt;  EdgeUse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; radial_prev;
&lt;&#x2F;span&gt;&lt;span&gt;  EdgeUse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; radial_next;
&lt;&#x2F;span&gt;&lt;span&gt;  
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6272a4;&quot;&gt;&#x2F;&#x2F; face loop (circularly linked list)
&lt;&#x2F;span&gt;&lt;span&gt;  EdgeUse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; loop_prev;
&lt;&#x2F;span&gt;&lt;span&gt;  EdgeUse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; loop_next;
&lt;&#x2F;span&gt;&lt;span&gt;  Vertex&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff79c6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; loop_vertex;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This gives us the final structure:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#282a36;color:#f8f8f2;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;font-style:italic;color:#8be9fd;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;EditableMesh&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  Pool&amp;lt;Vertex&amp;gt; vertices;
&lt;&#x2F;span&gt;&lt;span&gt;  Pool&amp;lt;Edge&amp;gt; edges;
&lt;&#x2F;span&gt;&lt;span&gt;  Pool&amp;lt;Face&amp;gt; faces;
&lt;&#x2F;span&gt;&lt;span&gt;  Pool&amp;lt;VertexUse&amp;gt; vertex_uses;
&lt;&#x2F;span&gt;&lt;span&gt;  Pool&amp;lt;EdgeUse&amp;gt; edge_uses;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#conclusion&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;This post presents a simple, performant non-manifold polygon mesh data structure, that enables topological &#x2F; adjacency queries.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to try out the final result:&lt;&#x2F;p&gt;
&lt;div class=&quot;app-download-card&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;apps.apple.com&#x2F;app&#x2F;shapereality-3d-modeling&#x2F;id6746662557&quot; target=&quot;_blank&quot;&gt;
        &lt;div class=&quot;app-card-content&quot;&gt;
            &lt;img src=&quot;&#x2F;images&#x2F;AppIcon.svg&quot; alt=&quot;ShapeReality App Icon&quot; class=&quot;app-icon&quot; &#x2F;&gt;
            &lt;div class=&quot;app-card-text&quot;&gt;
                &lt;p class=&quot;app-name&quot;&gt;ShapeReality - 3D Modeling&lt;&#x2F;p&gt;
                &lt;p class=&quot;app-description&quot;&gt;ShapeReality - 3D Modeling brings 3D polygon mesh modeling tools to your iPhone and iPad. Inspect, edit, save and share .obj files.&lt;&#x2F;p&gt;
            &lt;&#x2F;div&gt;
            &lt;img src=&quot;&#x2F;images&#x2F;download_appstore.svg&quot; alt=&quot;Download on the App Store&quot; class=&quot;appstore-badge&quot; &#x2F;&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;style&gt;
.app-download-card {
    width: 100%;
    background-color: #fff;
    border-radius: 16px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);
    margin: 0 auto;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
    border-color:#666;
    border-width:2px;
    border-style:dotted;
    padding: 1rem;
}

.app-card-content {
    display: flex;
    align-items: top;
    gap: 20px;
    flex-wrap: wrap;
}

.app-icon {
    width: 80px;
    height: 80px;
    flex-shrink: 0;
    margin: 0 !important;
}

.app-card-text {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-width: 0;
    flex: 1;
}

.app-name {
    font-size: 20px;
    font-weight: 600;
    margin: 0 0 2px !important;
    color: #111;
    line-height: 1.2;
}

.app-description {
    font-size: 14px;
    color: #666;
    margin: 0 0 12px !important;
}

.appstore-badge {
    width: 140px;
    height: auto;
}

@media (max-width: 600px) {
    .app-card-content {
        flex-direction: column;
        align-items: center;
        text-align: center;
    }

    .app-card-text {
        align-items: center;
    }
}
&lt;&#x2F;style&gt;</content>
        
    </entry>
    <entry xml:lang="en">
        <title>ShapeReality - 3D Modeling now available on iPhone 🎉</title>
        <published>2025-07-28T00:00:00+00:00</published>
        <updated>2025-07-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.shapereality.io/posts/ios-launch/"/>
        <id>https://blog.shapereality.io/posts/ios-launch/</id>
        
        <content type="html" xml:base="https://blog.shapereality.io/posts/ios-launch/">&lt;p&gt;For the past 1.5 month I have been working on a 3D modeling app for iOS devices using C++ and Swift, and I just released it on iOS.&lt;&#x2F;p&gt;
&lt;p&gt;It supports:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Non-manifold polygon meshes with loose vertices and edges (see &lt;a href=&quot;&#x2F;posts&#x2F;editable-mesh&quot;&gt;An editable non-manifold polygon mesh data structure&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Inserting vertices, edges and faces&lt;&#x2F;li&gt;
&lt;li&gt;Extruding edges and faces&lt;&#x2F;li&gt;
&lt;li&gt;Deleting elements&lt;&#x2F;li&gt;
&lt;li&gt;Duplicating elements&lt;&#x2F;li&gt;
&lt;li&gt;Multi element selection&lt;&#x2F;li&gt;
&lt;li&gt;Flipping face normals&lt;&#x2F;li&gt;
&lt;li&gt;Insert primitive shapes&lt;&#x2F;li&gt;
&lt;li&gt;Saving and loading to and from .obj (note that all geometry will be merged into one mesh)&lt;&#x2F;li&gt;
&lt;li&gt;Orbit camera viewport navigation&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The app is currently in its infancy, so I&#x27;m looking for feedback to prioritize which features to build and what to improve. Send me an email at:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;mailto:contact@shapereality.io&quot;&gt;contact@shapereality.io&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;timelapse-demo-of-using-the-app&quot;&gt;Timelapse demo of using the app:&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#timelapse-demo-of-using-the-app&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;div class=&quot;youtube-video-wrapper&quot;&gt;
&lt;iframe src=&quot;https:&#x2F;&#x2F;www.youtube-nocookie.com&#x2F;embed&#x2F;XG_bNcqe5zo&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;&lt;&#x2F;iframe&gt;
&lt;&#x2F;div&gt;
&lt;style&gt;
    .youtube-video-wrapper{
        position:relative; 
        padding-bottom:56.25%; 
        height: 0; 
        overflow:hidden;
        border-radius: 1rem; margin: 1rem 0;
    }
    .youtube-video-wrapper &gt; iframe{
        position: absolute;
        width: 100%;
        height: 100%;
    }
&lt;&#x2F;style&gt;&lt;h2 id=&quot;download-now&quot;&gt;Download now&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#download-now&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;div class=&quot;app-download-card&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;apps.apple.com&#x2F;app&#x2F;shapereality-3d-modeling&#x2F;id6746662557&quot; target=&quot;_blank&quot;&gt;
        &lt;div class=&quot;app-card-content&quot;&gt;
            &lt;img src=&quot;&#x2F;images&#x2F;AppIcon.svg&quot; alt=&quot;ShapeReality App Icon&quot; class=&quot;app-icon&quot; &#x2F;&gt;
            &lt;div class=&quot;app-card-text&quot;&gt;
                &lt;p class=&quot;app-name&quot;&gt;ShapeReality - 3D Modeling&lt;&#x2F;p&gt;
                &lt;p class=&quot;app-description&quot;&gt;ShapeReality - 3D Modeling brings 3D polygon mesh modeling tools to your iPhone and iPad. Inspect, edit, save and share .obj files.&lt;&#x2F;p&gt;
            &lt;&#x2F;div&gt;
            &lt;img src=&quot;&#x2F;images&#x2F;download_appstore.svg&quot; alt=&quot;Download on the App Store&quot; class=&quot;appstore-badge&quot; &#x2F;&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;style&gt;
.app-download-card {
    width: 100%;
    background-color: #fff;
    border-radius: 16px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);
    margin: 0 auto;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
    border-color:#666;
    border-width:2px;
    border-style:dotted;
    padding: 1rem;
}

.app-card-content {
    display: flex;
    align-items: top;
    gap: 20px;
    flex-wrap: wrap;
}

.app-icon {
    width: 80px;
    height: 80px;
    flex-shrink: 0;
    margin: 0 !important;
}

.app-card-text {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-width: 0;
    flex: 1;
}

.app-name {
    font-size: 20px;
    font-weight: 600;
    margin: 0 0 2px !important;
    color: #111;
    line-height: 1.2;
}

.app-description {
    font-size: 14px;
    color: #666;
    margin: 0 0 12px !important;
}

.appstore-badge {
    width: 140px;
    height: auto;
}

@media (max-width: 600px) {
    .app-card-content {
        flex-direction: column;
        align-items: center;
        text-align: center;
    }

    .app-card-text {
        align-items: center;
    }
}
&lt;&#x2F;style&gt;</content>
        
    </entry>
</feed>
