55 < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
66 < title > Stacktower: An Accidental Deep Dive</ title >
77 < meta name ="description " content ="How an XKCD comic led to teaching myself graph theory — a journey through NP-hard problems, PQ-trees, and layered graph algorithms. ">
8+ < meta name ="author " content ="Matthias Huels ">
9+ < meta name ="keywords " content ="dependency visualization, graph algorithms, PQ-tree, Sugiyama, layered graph drawing, XKCD, software dependencies, Go, npm, PyPI, crates.io ">
10+ < link rel ="canonical " href ="https://stacktower.io/ ">
11+
12+ <!-- Open Graph / Facebook -->
13+ < meta property ="og:type " content ="article ">
14+ < meta property ="og:url " content ="https://stacktower.io/ ">
15+ < meta property ="og:title " content ="Stacktower: An Accidental Deep Dive ">
16+ < meta property ="og:description " content ="How an XKCD comic led to teaching myself graph theory — a journey through NP-hard problems, PQ-trees, and layered graph algorithms. ">
17+ < meta property ="og:image " content ="https://stacktower.io/android-chrome-512x512.png ">
18+ < meta property ="og:site_name " content ="Stacktower ">
19+ < meta property ="article:author " content ="Matthias Huels ">
20+ < meta property ="article:published_time " content ="2025-10-01 ">
21+
22+ <!-- Twitter Card -->
23+ < meta name ="twitter:card " content ="summary_large_image ">
24+ < meta name ="twitter:url " content ="https://stacktower.io/ ">
25+ < meta name ="twitter:title " content ="Stacktower: An Accidental Deep Dive ">
26+ < meta name ="twitter:description " content ="How an XKCD comic led to teaching myself graph theory — a journey through NP-hard problems, PQ-trees, and layered graph algorithms. ">
27+ < meta name ="twitter:image " content ="https://stacktower.io/android-chrome-512x512.png ">
828
929 <!-- Favicons -->
1030 < link rel ="apple-touch-icon " sizes ="180x180 " href ="apple-touch-icon.png ">
1535 < link rel ="stylesheet " href ="styles.css ">
1636 < link rel ="preconnect " href ="https://fonts.googleapis.com ">
1737 < link rel ="preconnect " href ="https://fonts.gstatic.com " crossorigin >
38+ < link rel ="preconnect " href ="https://cdn.jsdelivr.net " crossorigin >
39+ < meta name ="theme-color " content ="#1a1a2e ">
1840 < link href ="https://fonts.googleapis.com/css2?family=Crimson+Text:ital,wght@0,400;0,600;0,700;1,400&family=JetBrains+Mono:wght@400;500;600&display=swap " rel ="stylesheet ">
1941
2042 <!-- MathJax for LaTeX rendering -->
3153 }
3254 } ;
3355 </ script >
34- < script src ="https://polyfill.io/v3/polyfill.min.js?features=es6 "> </ script >
3556 < script id ="MathJax-script " async src ="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js "> </ script >
3657
3758 <!-- Overtracking Pixel Code -->
3859 < script defer src ="https://cdn.overtracking.com/t/tEi6HMQbt5wJf9Yp8/ "> </ script >
3960 <!-- End Overtracking Pixel Code -->
61+
62+ <!-- Structured Data (JSON-LD) -->
63+ < script type ="application/ld+json ">
64+ {
65+ "@context" : "https://schema.org" ,
66+ "@type" : "Article" ,
67+ "headline" : "Stacktower: An Accidental Deep Dive" ,
68+ "description" : "How an XKCD comic led to teaching myself graph theory — a journey through NP-hard problems, PQ-trees, and layered graph algorithms." ,
69+ "image" : "https://stacktower.io/android-chrome-512x512.png" ,
70+ "author" : {
71+ "@type" : "Person" ,
72+ "name" : "Matthias Huels" ,
73+ "url" : "https://huels.ai"
74+ } ,
75+ "publisher" : {
76+ "@type" : "Person" ,
77+ "name" : "Matthias Huels" ,
78+ "url" : "https://huels.ai"
79+ } ,
80+ "datePublished" : "2025-10-01" ,
81+ "dateModified" : "2025-10-01" ,
82+ "mainEntityOfPage" : {
83+ "@type" : "WebPage" ,
84+ "@id" : "https://stacktower.io/"
85+ } ,
86+ "keywords" : [ "dependency visualization" , "graph algorithms" , "PQ-tree" , "Sugiyama framework" , "layered graph drawing" , "software dependencies" ]
87+ }
88+ </ script >
4089</ head >
4190< body >
4291 <!-- Main Navigation Tabs -->
@@ -127,11 +176,11 @@ <h2>Welcome to the Side Quest</h2>
127176
128177 < div class ="diagram-row ">
129178 < div class ="diagram-item ">
130- < img src ="plots/simple_dag.svg " alt ="Node-link diagram ">
179+ < img src ="plots/simple_dag.svg " alt ="Node-link diagram " loading =" lazy " >
131180 < p > < em > < strong > Fig. 1 (a): </ strong > Traditional node-link diagram with explicit arrows</ em > </ p >
132181 </ div >
133182 < div class ="diagram-item ">
134- < img src ="plots/simple_tower.svg " alt ="Tower diagram ">
183+ < img src ="plots/simple_tower.svg " alt ="Tower diagram " loading =" lazy " >
135184 < p > < em > < strong > Fig. 1 (b): </ strong > Tower layout where contact implies dependency</ em > </ p >
136185 </ div >
137186 </ div >
@@ -199,15 +248,15 @@ <h3>Step 1: Transitive Cleanup</h3>
199248
200249 < div class ="diagram-row ">
201250 < div class ="diagram-item ">
202- < img src ="plots/transitive_before.svg " alt ="Before transitive reduction ">
251+ < img src ="plots/transitive_before.svg " alt ="Before transitive reduction " loading =" lazy " >
203252 < p > < em > < strong > Fig. 2 (a): </ strong > The transitive trap</ em > </ p >
204253 </ div >
205254 < div class ="diagram-item ">
206- < img src ="plots/transitive_after.svg " alt ="After transitive reduction ">
255+ < img src ="plots/transitive_after.svg " alt ="After transitive reduction " loading =" lazy " >
207256 < p > < em > < strong > Fig. 2 (b): </ strong > After transitive reduction</ em > </ p >
208257 </ div >
209258 < div class ="diagram-item ">
210- < img src ="plots/transitive_after_tower.svg " alt ="Tower view ">
259+ < img src ="plots/transitive_after_tower.svg " alt ="Tower view " loading =" lazy " >
211260 < p > < em > < strong > Fig. 2 (c): </ strong > Stacked tower view</ em > </ p >
212261 </ div >
213262 </ div >
@@ -220,11 +269,11 @@ <h3>Step 2: Edge Shortening</h3>
220269
221270 < div class ="diagram-row ">
222271 < div class ="diagram-item ">
223- < img src ="plots/dummies_before.svg " alt ="Before dummy insertion ">
272+ < img src ="plots/dummies_before.svg " alt ="Before dummy insertion " loading =" lazy " >
224273 < p > < em > < strong > Fig. 3 (a): </ strong > Long-spanning edge</ em > </ p >
225274 </ div >
226275 < div class ="diagram-item ">
227- < img src ="plots/dummies_after.svg " alt ="After dummy insertion ">
276+ < img src ="plots/dummies_after.svg " alt ="After dummy insertion " loading =" lazy " >
228277 < p > < em > < strong > Fig. 3 (b): </ strong > Bridged with dummies</ em > </ p >
229278 </ div >
230279 </ div >
@@ -233,11 +282,11 @@ <h3>Step 2: Edge Shortening</h3>
233282
234283 < div class ="diagram-row ">
235284 < div class ="diagram-item ">
236- < img src ="plots/dummies_after_tower.svg " alt ="After tower ">
285+ < img src ="plots/dummies_after_tower.svg " alt ="After tower " loading =" lazy " >
237286 < p > < em > < strong > Fig. 4 (a): </ strong > Intermediate tower view</ em > </ p >
238287 </ div >
239288 < div class ="diagram-item ">
240- < img src ="plots/dummies_before_tower.svg " alt ="Before tower ">
289+ < img src ="plots/dummies_before_tower.svg " alt ="Before tower " loading =" lazy " >
241290 < p > < em > < strong > Fig. 4 (b): </ strong > Final merged view</ em > </ p >
242291 </ div >
243292 </ div >
@@ -248,7 +297,7 @@ <h3>Step 3: Planarity Repair</h3>
248297
249298 < div class ="diagram-row " style ="justify-content: center; ">
250299 < div class ="diagram-item ">
251- < img src ="plots/separators_before.svg " alt ="Before separator insertion ">
300+ < img src ="plots/separators_before.svg " alt ="Before separator insertion " loading =" lazy " >
252301 < p > < em > < strong > Fig. 5: </ strong > Tangled edges</ em > </ p >
253302 </ div >
254303 </ div >
@@ -257,11 +306,11 @@ <h3>Step 3: Planarity Repair</h3>
257306
258307 < div class ="diagram-row " style ="justify-content: center; ">
259308 < div class ="diagram-item ">
260- < img src ="plots/separators_after.svg " alt ="After separator insertion ">
309+ < img src ="plots/separators_after.svg " alt ="After separator insertion " loading =" lazy " >
261310 < p > < em > < strong > Fig. 6 (a): </ strong > Separator node insertion</ em > </ p >
262311 </ div >
263312 < div class ="diagram-item ">
264- < img src ="plots/separators_after_tower.svg " alt ="Tower view ">
313+ < img src ="plots/separators_after_tower.svg " alt ="Tower view " loading =" lazy " >
265314 < p > < em > < strong > Fig. 6 (b): </ strong > Tower view</ em > </ p >
266315 </ div >
267316 </ div >
@@ -375,11 +424,11 @@ <h3>Attempt #2: The PQ-Tree</h3>
375424
376425 < div class ="diagram-row ">
377426 < div class ="diagram-item ">
378- < img src ="plots/pq_tree_before.svg " alt ="PQ-tree before applying adjacency constraint ">
427+ < img src ="plots/pq_tree_before.svg " alt ="PQ-tree before applying adjacency constraint " loading =" lazy " >
379428 < p > < em > < strong > Fig. 7 (a): </ strong > Single P-node with Logging, Auth, Caching</ em > </ p >
380429 </ div >
381430 < div class ="diagram-item ">
382- < img src ="plots/pq_tree_after.svg " alt ="PQ-tree after applying adjacency constraint ">
431+ < img src ="plots/pq_tree_after.svg " alt ="PQ-tree after applying adjacency constraint " loading =" lazy " >
383432 < p > < em > < strong > Fig. 7 (b): </ strong > Q-node enforcing [Auth, Caching] adjacency</ em > </ p >
384433 </ div >
385434 </ div >
0 commit comments