<?xml version='1.0' encoding='utf-8'?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://blog.wunderdev.com/</id>
  <title><![CDATA[Wunderdev]]></title>
  <subtitle><![CDATA[A personal blog about my hobby projects]]></subtitle>
  <icon>https://blog.wunderdev.com/favicon.png</icon>
  <link href="https://blog.wunderdev.com" />
  <link href="https://blog.wunderdev.com/atom.xml" rel="self" type="application/atom+xml" />
  <updated>2024-12-16T12:48:43.830Z</updated>
  <author>
    <name><![CDATA[Marcus Münger]]></name>
  </author>
  <category term="svelte" scheme="https://blog.wunderdev.com/?tags=svelte" />
  <category term="css" scheme="https://blog.wunderdev.com/?tags=css" />
  <category term="three js" scheme="https://blog.wunderdev.com/?tags=three%20js" />
  <category term="firefox" scheme="https://blog.wunderdev.com/?tags=firefox" />
  <category term="chromium" scheme="https://blog.wunderdev.com/?tags=chromium" />
  <category term="Docker" scheme="https://blog.wunderdev.com/?tags=Docker" />
  <category term="tutorial" scheme="https://blog.wunderdev.com/?tags=tutorial" />
  <category term="wing" scheme="https://blog.wunderdev.com/?tags=wing" />
  <category term="language design" scheme="https://blog.wunderdev.com/?tags=language%20design" />
  <category term="kanlang" scheme="https://blog.wunderdev.com/?tags=kanlang" />
  <category term="compiler" scheme="https://blog.wunderdev.com/?tags=compiler" />
  <category term="Interviews" scheme="https://blog.wunderdev.com/?tags=Interviews" />
  <category term="k8s" scheme="https://blog.wunderdev.com/?tags=k8s" />
  <category term="knative" scheme="https://blog.wunderdev.com/?tags=knative" />
  <category term="github pages" scheme="https://blog.wunderdev.com/?tags=github%20pages" />
  <category term="procedural generation" scheme="https://blog.wunderdev.com/?tags=procedural%20generation" />
  <category term="typescript" scheme="https://blog.wunderdev.com/?tags=typescript" />
  <category term="wave function collapse" scheme="https://blog.wunderdev.com/?tags=wave%20function%20collapse" />
  <category term="algorithm" scheme="https://blog.wunderdev.com/?tags=algorithm" />
  <category term="DnD" scheme="https://blog.wunderdev.com/?tags=DnD" />
  <category term="javascript" scheme="https://blog.wunderdev.com/?tags=javascript" />
  <category term="threlte" scheme="https://blog.wunderdev.com/?tags=threlte" />
  <category term="rapier" scheme="https://blog.wunderdev.com/?tags=rapier" />
  <category term="nearley" scheme="https://blog.wunderdev.com/?tags=nearley" />
  <category term="query" scheme="https://blog.wunderdev.com/?tags=query" />
  <category term="data" scheme="https://blog.wunderdev.com/?tags=data" />
  <category term="yaml" scheme="https://blog.wunderdev.com/?tags=yaml" />
  <category term="language learning" scheme="https://blog.wunderdev.com/?tags=language%20learning" />
  <category term="japanese" scheme="https://blog.wunderdev.com/?tags=japanese" />
  <category term="linitic" scheme="https://blog.wunderdev.com/?tags=linitic" />
  <entry>
    <title type="html"><![CDATA[Svelte class prefix]]></title>
    <link href="https://blog.wunderdev.com/blog/svelte/1" />
    <id>https://blog.wunderdev.com/blog/svelte/1</id>
    <published>2024-03-22T00:00:00.000Z</published>
    <updated>2024-12-16T12:47:01.446Z</updated>
    <summary type="html"><![CDATA[Adding class prefixes in svelte]]></summary>
    <content type="html">
      <![CDATA[<p>I recently came across an issue with svelte, and though I would share my finding here so that anyone in the future with the same problem might stumble upon it.</p><p>So imagine you are writing a svelte component, and adding some styling to it.You might create a class named “panel” and use it in your markup. <code>panel</code> is a pretty generic class name, but svelte will create a hash for your component and make sure that your new <code>panel</code> class only applies to elements that also have the svelte hash as a class. I think this is a really smart way of solving css isolation. But the issue is that it only goes in one direction. If we assume that we are in a complex systems with multiple frameworks fighting for attention, it seems not unlikely that someone else has defined a class named <code>panel</code>. Svelte will of course not modify that component, but since svelte doesn’t change the name of your classes we will inherit the styling of <code>panel</code> defined outside of our component.</p><p>The issue is in sveltes defense, not a common one and not one I would like the maintainers to fix. And actually it is not even a bad thing that it works this way because it allows us to import an outdated css framework such as bootstrap and use their classes.</p><p>But ok, let us say we want to fix it!We can write a plugin that prefixes all of our classes, so that <code>panel</code> becomes <code>prefix-panel</code> and in the case of global styles, we can “import” them by writing <code>@panel</code> and then in the plugin we strip the <code>@</code> sign, thus allowing global css styles to affect it</p><!-- HTML_TAG_START --><pre class="shiki material-default" ts="true"><div class="language-id">ts</div><div class='code-container'><code><div class='line'>import &#123; vitePreprocess &#125; from '@sveltejs/vite-plugin-svelte'</div><div class='line'></div><div class='line'>import translations from "./Scripts/Svelte/translationExtractor.js"</div><div class='line'></div><div class='line'>import &#123; compile, parse, walk &#125; from 'svelte/compiler';</div><div class='line'></div><div class='line'>import MagicString from 'magic-string';</div><div class='line'></div><div class='line'>export default &#123;</div><div class='line'>  preprocess: [vitePreprocess(),</div><div class='line'>  &#123;</div><div class='line'>    name: "class prefixer",</div><div class='line'>    markup: (&#123; content, filename &#125;) =&gt; &#123;</div><div class='line'>      let result = new MagicString(content, &#123; filename &#125;);</div><div class='line'>      const ast = parse(content, &#123; filename &#125;)</div><div class='line'>      walk(ast, &#123;</div><div class='line'>        enter(node, parent, prop, index) &#123;</div><div class='line'>          const data = &#123;</div><div class='line'>            ...node,</div><div class='line'>            text: content.substring(node.start, node.end)</div><div class='line'>          &#125;</div><div class='line'>          if (node.type === "Attribute" && node.name === "class") &#123;</div><div class='line'>            for (let c of node.value) &#123;</div><div class='line'>              if (c.data) &#123;</div><div class='line'>                result.overwrite(c.start, c.end, c.data.split(" ").map(c =&gt; c.startsWith("@") ? c.substring(1) : ("prefix-" + c)).join(" "))</div><div class='line'>              &#125;</div><div class='line'>            &#125;</div><div class='line'>          &#125;</div><div class='line'>          if (node.type === "ClassSelector" && !node.name.startsWith("@")) &#123;</div><div class='line'>            if (node.name.startsWith("@")) &#123;</div><div class='line'>              result.overwrite(node.start, node.end, "." + node.name.substring(1))</div><div class='line'>            &#125;</div><div class='line'>            else &#123;</div><div class='line'>              result.overwrite(node.start, node.end, ".prefix-" + node.name)</div><div class='line'>            &#125;</div><div class='line'>          &#125;</div><div class='line'>          if (node.type === "Class") &#123;</div><div class='line'>            const start = node.start + "class:".length</div><div class='line'>            const end = node.start + "class:".length + node.name.length</div><div class='line'>            if (node.expression.type === "Identifier" && node.end == end) &#123;</div><div class='line'>              result.overwrite(start, end, &#96;prefix-$&#123;node.name&#125;=&#123;$&#123;node.expression.name&#125;&#125;&#96;)</div><div class='line'>            &#125;</div><div class='line'>            else &#123;</div><div class='line'>              result.overwrite(start, end, "prefix-" + node.name)</div><div class='line'>            &#125;</div><div class='line'>          &#125;</div><div class='line'>        &#125;</div><div class='line'>      &#125;)</div><div class='line'>      return &#123;</div><div class='line'>        code: result.toString(),</div><div class='line'>        map: result.generateMap()</div><div class='line'>      &#125;;</div><div class='line'>    &#125;,</div><div class='line'>  &#125;</div><div class='line'>  ]</div><div class='line'>&#125;</div></code></div></pre><!-- HTML_TAG_END --><p>The heavy lifting of course comes from the great plugin support from svelte and the access to its’ compile/parse methods, and <code>MagicString</code> which is a great package for updating multiple places in a string (a package that I will likely put to use in the future)</p>]]>
    </content>
    <category term="svelte" scheme="https://blog.wunderdev.com/?tags=svelte" />
    <category term="css" scheme="https://blog.wunderdev.com/?tags=css" />
  </entry>
  <entry>
    <title type="html"><![CDATA[Rounding in firefox and chromium]]></title>
    <link href="https://blog.wunderdev.com/blog/threejs/2" />
    <id>https://blog.wunderdev.com/blog/threejs/2</id>
    <published>2024-03-22T00:00:00.000Z</published>
    <updated>2024-12-16T12:47:01.474Z</updated>
    <summary type="html"><![CDATA[Rounding errors for three js in chromium vs firefox]]></summary>
    <content type="html">
      <![CDATA[<p>I encountered an interesting bug today that I thought to write about.</p><p>This bug relates to ThreeJS and as a 3D framework it of course handles matrixes, and float values.The fact that I explicitly say float values, might tick you of that this bug relates to a rounding error, and you would be correct in thinking so.</p><p>Imagine setting the position and rotation of a mesh.</p><!-- HTML_TAG_START --><pre class="shiki material-default"><div class='code-container'><code><div class='line'>const test = new THREE.Mesh(new THREE.PlaneGeometry(), new THREE.MeshBasicMaterial());</div><div class='line'>test.position.copy(new THREE.Vector3(-0.0800582263098657,0,-0.003907569688712309))</div><div class='line'>test.rotation.copy(new THREE.Euler(-1.5707963267948966,0,0, "XYZ"))</div></code></div></pre><!-- HTML_TAG_END --><p>Nothing out of the ordinary, and since we are making it easy for us we aren’t manually doing anything with the meshes matrix, and let threeJS do that for us on the next render cycle.And this will likely work! at least it did for me. But it turns out that it only worked for me because I am running a chromium based browser, which runs the V8 js engine.When I tried my code in firefox, running SpiderMonkey, everything was renderer upside down because of a rounding error. Or rather, everything that I projected using a raycast was renderer upside down.</p><p>What is more interesting (in my opinion) is that the rounding error is really small, and the diff between V8 and SpiderMonkey came down to about <code>4e-16</code> in a cell in the meshes matrix.But <code>4e-16</code> can be enough to flip a sign from plus to minus, which it did in my case, resulting in the raycast getting flipped.</p><h2 id="so-can-we-fix-it"><a href="#so-can-we-fix-it">So can we fix it?</a></h2><p>I made my own solution to this, and it is not pretty, but it works.</p><!-- HTML_TAG_START --><pre class="shiki material-default" ts="true"><div class="language-id">ts</div><div class='code-container'><code><div class='line'>private setMatrixPrecisionOnMesh(mesh: THREE.Object3D) &#123;</div><div class='line'>    const update = mesh.updateMatrix;</div><div class='line'>    mesh.updateMatrix = function updateMatrix() &#123;</div><div class='line'>        const precision = 10000000</div><div class='line'>        const round = (num) =&gt; Math.round(num * precision) / precision;</div><div class='line'>        this.position.setX(round(this.position.x));</div><div class='line'>        this.position.setY(round(this.position.y));</div><div class='line'>        this.position.setZ(round(this.position.z));</div><div class='line'>        this.rotation.set(</div><div class='line'>            round(this.rotation.x),</div><div class='line'>            round(this.rotation.y),</div><div class='line'>            round(this.rotation.z),</div><div class='line'>            this.rotation.order</div><div class='line'>        )</div><div class='line'>        update.call(this, &#123;&#125;);</div><div class='line'>    &#125;</div><div class='line'>&#125;</div></code></div></pre><!-- HTML_TAG_END --><p>Using that method I get numbers that aren’t as precise as before, but I get rid of all rounding differences between firefox and chromium.</p>]]>
    </content>
    <category term="three js" scheme="https://blog.wunderdev.com/?tags=three%20js" />
    <category term="firefox" scheme="https://blog.wunderdev.com/?tags=firefox" />
    <category term="chromium" scheme="https://blog.wunderdev.com/?tags=chromium" />
  </entry>
  <entry>
    <title type="html"><![CDATA[Works on my machine]]></title>
    <link href="https://blog.wunderdev.com/blog/docker/1" />
    <id>https://blog.wunderdev.com/blog/docker/1</id>
    <published>2023-09-25T00:00:00.000Z</published>
    <updated>2024-12-16T12:47:01.374Z</updated>
    <summary type="html"><![CDATA[Why the promise of docker is a lie, and why it isn't]]></summary>
    <content type="html">
      <![CDATA[<p>Have you been sold the promise that Docker will solve the ancient issue of “works on my machine”?And have you faced issues with your local environment that uses docker, and questioned, “why isn’t it working on my machine?”</p><p>It is not unlikely that you have fallen into this trap and wondered why docker is being sold that way, and I am hoping to shed some light on the issue with this post.</p><h1 id="production"><a href="#production">Production</a></h1><p>OK, lets begin with the place that is the most important, Prod!</p><p>In the times before Docker, we threw a bundled package over the wall to the ops team and they moved the package over to a prod server and hit the restart button.For the majority of deployments this worked without issues. <em>But</em> it wasn’t perfect. What happened when for instance you upgraded java version?</p><p>Let us assume that you had a legacy system that you upgraded from java 8 to java 11. On your local development environment you installed java 11, and made sure everything was working, then you once again threw the bundle to the ops team, and then it got tricky for them. Of course they too needed to install java 11, and they probably needed to change some service definitions to use java 11 instead of 8. So far so good, it <em>should</em> work, even though it was a bit of a hassle. But then it breaks! And you start questioning why it breaks. The same code works on your machine, how come it doesn’t in prod? Turns out you installed oracle JDK11 on your machine, and you only specified java 11 to the ops team, who went ahead and installed GraalVM, and now production is broken.</p><p>This hypothetical might not be the biggest of issues, and it can be mitigated in more than one way. But the fact that the underlying systems aren’t working the same way is a massive issue. Another pretty clear example would be if the prod servers are running linux and you are running mac. They are both unix systems so you have access to a lot of similar tools. And you might think that both have access to the <code>sed</code> command since it is in the path on both machines… <em>but</em> of course they don’t work the same, unless you had installed <code>gsed</code> on mac. Small things like that are why we need docker.</p><h1 id="local-dev"><a href="#local-dev">Local dev</a></h1><p>Ok, so you have dockerized your production environment, great!And you made a deployment that for whatever reason is not behaving like you expected. So you start debugging it, by checking out the same git commit as prod is running and you run something like <code>docker built -t app . &amp;&amp; docker run -it app</code> (build and start the docker image).Some progress bars start flying across the screen and then you get an error message and the build halts.Weird, right? it runs in production so this should work. You ask your desk neighbor to do the same and he says “works on my machine”.At this point you start blaming docker, and question what the point is, since it doesn’t solve the core issue!</p><p>But here is the thing: The works on my machine issue can show itself in two places. When running and when building.If you would have skipped the build step and instead just ran <code>docker run -it app:prod</code> it is likely to have worked.Or at the very least it would behave the same as the prod version, but it could of course still crash because of networking issues, access problems, or just plainly wrong environment variables.</p><p>So why was it working for your desk neighbor and not for you?There could be tons of reasons for this, but for instance you can have different docker layers in your caches. Or, you could have different files on disk.For example your project could have important but (to git) ignored files, and when you are adding files to your docker layer you can get a diff.</p><h1 id="conclusion"><a href="#conclusion">Conclusion</a></h1><p>So does docker solve the “works on my machine” problem?</p><p>Without a question it does solve that problem in some aspects. It just doesn’t solve it in all places you might wish it does.Docker is in no way a magic bullet, but you can be sure that if you test an image in a test environment and send <strong>the same</strong> image to production you can be sure that the code will behave the same way and any issues are likely to be related to configuration or other supporting systems.Key point here is to use the <strong>same image</strong>. If you are rebuilding a new image for production it will in essence run untested code.</p>]]>
    </content>
    <category term="Docker" scheme="https://blog.wunderdev.com/?tags=Docker" />
  </entry>
  <entry>
    <title type="html"><![CDATA[Pasting decals in Three JS]]></title>
    <link href="https://blog.wunderdev.com/blog/threejs/1" />
    <id>https://blog.wunderdev.com/blog/threejs/1</id>
    <published>2023-09-21T00:00:00.000Z</published>
    <updated>2024-12-16T12:47:01.466Z</updated>
    <summary type="html"><![CDATA[Three JS tutorial on how to place decals or stickers on an object]]></summary>
    <content type="html">
      <![CDATA[<p>Have you ever wanted to paste some stickers on a 3D object?Perhaps to check what your product would look like with a sticker on it and check where the best placement is without actually placing a sticker on a real product.</p><p>You can of course do this with some pretty minor Photoshop skills, but let us dive into <a href="https://threejs.org/" rel="nofollow noopener noreferrer external" target="_blank">three js</a> and do it the hard way instead!</p><h2 id="intro"><a href="#intro">Intro</a></h2><p>I will be using svelte and the three js wrapper <a href="https://threlte.xyz/" rel="nofollow noopener noreferrer external" target="_blank">threlte</a> as that is the most convenient for me. But the concepts should translate pretty well to vanilla three js.</p><p>To start of, make sure you have your project setup and installed according to respective instructions.</p><h2 id="app"><a href="#app">App</a></h2><p>Best practice according to threlte is to separate your canvas and your scene, so let’s do that.</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" svelte="true" title="App.svelte"><div class='code-title'>App.svelte</div><div class="language-id">svelte</div><div class='code-container'><code><div class='line'>&lt;script&gt;</div><div class='line'>  import &#123; Canvas &#125; from '@threlte/core'</div><div class='line'>  import Scene from '$lib/Scene.svelte'</div><div class='line'>&lt;/script&gt;</div><div class='line'></div><div class='line'>&lt;div class="app"&gt;</div><div class='line'>  &lt;Canvas&gt;</div><div class='line'>    &lt;Scene /&gt;</div><div class='line'>  &lt;/Canvas&gt;</div><div class='line'>&lt;/div&gt;</div></code></div></pre><!-- HTML_TAG_END --><p>And for the fun of it, we might as well slap on some styling on it</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" svelte="true" title="App.svelte"><div class='code-title'>App.svelte</div><div class="language-id">svelte</div><div class='code-container'><code><div class='line'>&lt;style&gt;</div><div class='line'>  .app &#123;</div><div class='line'>    width: 100%;</div><div class='line'>    height: 100%;</div><div class='line'>    position: absolute;</div><div class='line'>    left: 0px;</div><div class='line'>    top: 0px;</div><div class='line'>    background: radial-gradient(circle, rgb(149, 154, 160) 0%, rgb(94, 100, 105) 100%);</div><div class='line'>    background-position: 0, 0;</div><div class='line'>  &#125;</div><div class='line'>&lt;/style&gt;</div></code></div></pre><!-- HTML_TAG_END --><h2 id="the-scene"><a href="#the-scene">The Scene</a></h2><p>We will do most of the heavy lifting here, but to keep the file succinct let us break it into parts.</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" svelte="true" title="Scene.svelte"><div class='code-title'>Scene.svelte</div><div class="language-id">svelte</div><div class='code-container'><code><div class='line'>&lt;script&gt;</div><div class='line'>  import Target from './Target.svelte'</div><div class='line'>  import Camera from './Camera.svelte'</div><div class='line'>  import Lights from './Lights.svelte'</div><div class='line'></div><div class='line'>  let camera</div><div class='line'>  let mesh</div><div class='line'>&lt;/script&gt;</div><div class='line'></div><div class='line'>&lt;Target bind:mesh /&gt;</div><div class='line'>&lt;Camera bind:camera /&gt;</div><div class='line'>&lt;Lights /&gt;</div></code></div></pre><!-- HTML_TAG_END --><p>I think this is a reasonable split. So we have a component <code>Target</code> to hold the model that we are to paste decals on. We have a <code>Camera</code> component that wraps the three JS camera. And lastly we have all our lighting in a <code>Lights</code> component.</p><p>We will add the decals and the controller for that in a bit.</p><h3 id="target"><a href="#target">Target</a></h3><p>For the sake of it I have modelled I water bottle in blender and exported it to <code>GLTF</code> which works quite well in my experience.</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" svelte="true" title="Target.svelte"><div class='code-title'>Target.svelte</div><div class="language-id">svelte</div><div class='code-container'><code><div class='line'>&lt;script&gt;</div><div class='line'>  import &#123; GLTF &#125; from '@threlte/extras'</div><div class='line'>  export let mesh</div><div class='line'>&lt;/script&gt;</div><div class='line'></div><div class='line'>&lt;GLTF</div><div class='line'>  useDraco="https://www.gstatic.com/draco/v1/decoders/"</div><div class='line'>  url="/bottle.glb"</div><div class='line'>  on:create=&#123;(&#123; ref &#125;) =&gt; &#123;</div><div class='line'>    mesh = ref.children[1]</div><div class='line'>  &#125;&#125; /&gt;</div></code></div></pre><!-- HTML_TAG_END --><p>noteworthy here is that the <code>GLTF</code> loader results in a group, and my code only handles a single mesh. So in a hacky proof of concept I am just picking the second child which is the canister of the bottle. In an ideal world you would either have the code handle group, or a list of meshes and just flatmap everything.</p><h3 id="camera"><a href="#camera">Camera</a></h3><p>We want a camera, and we want to be able to rotate it around the target.We could use an <a href="https://threejs.org/docs/#examples/en/controls/OrbitControls" rel="nofollow noopener noreferrer external" target="_blank"><code>OrbitControls</code></a> but I find it a bit limited, and couldn’t find a way to bind it to middle mouse instead of left click, that we will use for pasting decals.</p><p>so instead let us add this</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" svelte="true" title="Camera.svelte"><div class='code-title'>Camera.svelte</div><div class="language-id">svelte</div><div class='code-container'><code><div class='line'>&lt;script&gt;</div><div class='line'>  import &#123; T &#125; from '@threlte/core'</div><div class='line'>  export let camera</div><div class='line'></div><div class='line'>  let rotation = 75 //arbitrary number</div><div class='line'>  export function drag(e) &#123;</div><div class='line'>    if (isMiddleMouseDown) &#123;</div><div class='line'>      rotation += e.movementX</div><div class='line'>      rotateCamera()</div><div class='line'>    &#125;</div><div class='line'>  &#125;</div><div class='line'></div><div class='line'>  function rotateCamera() &#123;</div><div class='line'>    const sensitivity = 100</div><div class='line'>    const distance = 10</div><div class='line'>    const pos = [Math.sin(rotation / sensitivity), 1, Math.cos(rotation / sensitivity)].map(comp =&gt; comp * distance)</div><div class='line'>    camera.position.set(...pos)</div><div class='line'>    camera.lookAt(0, 1, 0)</div><div class='line'>    camera = camera //force svelte re-render</div><div class='line'>  &#125;</div><div class='line'></div><div class='line'>  let isMiddleMouseDown = false</div><div class='line'>  function mouseDown(e) &#123;</div><div class='line'>    isMiddleMouseDown = e.button === 1 //1 is middle mouse</div><div class='line'>  &#125;</div><div class='line'>  function mouseUp(e) &#123;</div><div class='line'>    isMiddleMouseDown = false</div><div class='line'>  &#125;</div><div class='line'>&lt;/script&gt;</div><div class='line'></div><div class='line'>&lt;svelte:window on:mousedown=&#123;mouseDown&#125; on:mouseup=&#123;mouseUp&#125; on:mousemove=&#123;drag&#125; /&gt;</div><div class='line'></div><div class='line'>&lt;T.PerspectiveCamera</div><div class='line'>  makeDefault</div><div class='line'>  bind:ref=&#123;camera&#125;</div><div class='line'>  on:create=&#123;() =&gt; &#123;</div><div class='line'>    rotateCamera()</div><div class='line'>  &#125;&#125; /&gt;</div></code></div></pre><!-- HTML_TAG_END --><h3 id="lights"><a href="#lights">Lights</a></h3><p>Ok last thing before we start working on the decals.We of course want to add lights to our target.</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" svelte="true" title="Lights.svelte"><div class='code-title'>Lights.svelte</div><div class="language-id">svelte</div><div class='code-container'><code><div class='line'>&lt;script&gt;</div><div class='line'>  import &#123; T &#125; from '@threlte/core'</div><div class='line'>  let l1Rot = 1</div><div class='line'>  let l2Rot = -1</div><div class='line'>&lt;/script&gt;</div><div class='line'></div><div class='line'>&lt;T.AmbientLight /&gt;</div><div class='line'>&lt;T.DirectionalLight position=&#123;[Math.sin(l1Rot) * 10, 10, Math.cos(l1Rot) * 10]&#125; castShadow intensity=&#123;0.8&#125; /&gt;</div><div class='line'>&lt;T.DirectionalLight position=&#123;[Math.sin(l2Rot) * 10, 10, Math.cos(l2Rot) * 10]&#125; intensity=&#123;0.2&#125; /&gt;</div></code></div></pre><!-- HTML_TAG_END --><p>Note that this has similar math as the camera to rotate the position of lights around the world origin.</p><h3 id="partial-result"><a href="#partial-result">Partial result</a></h3><p>We should have something looking like this now<picture><source srcset="/_app/immutable/assets/scene-1cb234b4.avif 736w" type="image/avif">  <img alt="scene" class="rounded-lg my-2" loading="lazy" decoding="async"></picture></p><h2 id="raycast"><a href="#raycast">Raycast</a></h2><p>Now the fun begins!We want to figure out if the mouse is hovering on our mesh and where.So with some code that is shamelessly stolen and modified from <a href="https://threejs.org/examples/#webgl_decals" rel="nofollow noopener noreferrer external" target="_blank">here</a></p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" js="true" title="Scene.svelte"><div class='code-title'>Scene.svelte</div><div class="language-id">js</div><div class='code-container'><code><div class='line'></div><div class='line'>let lineGeometry = new THREE.BufferGeometry()</div><div class='line'>lineGeometry.setFromPoints([new THREE.Vector3(), new THREE.Vector3()])</div><div class='line'>let line</div><div class='line'>const raycaster = new THREE.Raycaster()</div><div class='line'>const mouseHelper = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 10))</div><div class='line'>function checkIntersection(x, y) &#123;</div><div class='line'>  if (mesh === undefined) return</div><div class='line'></div><div class='line'>  const intersection = &#123;</div><div class='line'>    intersects: false,</div><div class='line'>    point: new THREE.Vector3(),</div><div class='line'>    normal: new THREE.Vector3()</div><div class='line'>  &#125;</div><div class='line'></div><div class='line'>  const mouse = new THREE.Vector2()</div><div class='line'>  mouse.x = (x / window.innerWidth) * 2 - 1</div><div class='line'>  mouse.y = -(y / window.innerHeight) * 2 + 1</div><div class='line'></div><div class='line'>  raycaster.setFromCamera(mouse, camera)</div><div class='line'>  const intersects = []</div><div class='line'>  raycaster.intersectObject(mesh, false, intersects)</div><div class='line'></div><div class='line'>  if (intersects.length &gt; 0) &#123;</div><div class='line'>    const p = intersects[0].point</div><div class='line'>    mouseHelper.position.copy(p)</div><div class='line'>    intersection.point.copy(p)</div><div class='line'></div><div class='line'>    const n = intersects[0].face.normal.clone()</div><div class='line'>    n.transformDirection(mesh.matrixWorld)</div><div class='line'>    n.multiplyScalar(10)</div><div class='line'>    n.add(intersects[0].point)</div><div class='line'></div><div class='line'>    intersection.normal.copy(intersects[0].face.normal)</div><div class='line'>    mouseHelper.lookAt(n)</div><div class='line'>    const pos = line.geometry.attributes.position</div><div class='line'>    pos.setXYZ(0, p.x, p.y, p.z)</div><div class='line'>    pos.setXYZ(1, n.x, n.y, n.z)</div><div class='line'>    pos.needsUpdate = true</div><div class='line'>    line = line</div><div class='line'></div><div class='line'>    intersection.intersects = true</div><div class='line'></div><div class='line'>    intersects.length = 0</div><div class='line'>  &#125; else &#123;</div><div class='line'>    intersection.intersects = false</div><div class='line'>  &#125;</div><div class='line'>  return intersection</div><div class='line'>&#125;</div><div class='line'>&lt;/script&gt;</div></code></div></pre><!-- HTML_TAG_END --><p>this will send out a imaginary line from the center of the camera and forwards into the world, targeting the x,y position of the mouse. If that line intersects our mesh we will get a list with places where it intersects. From that list we can fetch the coordinate in 3d space as well as the normal of the face. The normal of a polygon or a face is a normalized vector that indicates where the it is facing.</p><p>This code introduces a few more variables, so let us put them to use, and add a listener for mouse movement</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" svelte="true" title="Scene.svelte"><div class='code-title'>Scene.svelte</div><div class="language-id">svelte</div><div class='code-container'><code><div class='line'></div><div class='line'></div><div class='line'>  function mouseMove(e) &#123;</div><div class='line'>    const intersection = checkIntersection(e.clientX, e.clientY);</div><div class='line'>  &#125;</div><div class='line'>&lt;/script&gt;</div><div class='line'></div><div class='line'>&lt;svelte:window on:click=&#123;click&#125; on:mousemove=&#123;mouseMove&#125; /&gt;</div><div class='line'>&lt;T.Line bind:ref=&#123;line&#125; geometry=&#123;lineGeometry&#125; /&gt;</div></code></div></pre><!-- HTML_TAG_END --><p>This should give us a line pointing to the target, so that can verify that everything is working.</p><p><picture><source srcset="/_app/immutable/assets/line-e323b517.avif 736w" type="image/avif">  <img alt="line" class="rounded-lg my-2" loading="lazy" decoding="async"></picture></p><p>Note that the <code>mouseHelper</code> is not added to the scene. We only have it to make it easier to get the rotation of the decal.</p><h2 id="decals"><a href="#decals">Decals</a></h2><p>Now we know how to point to an object, we want to add the decals objects to it.</p><p>so let us add some code for that in the scene.</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" svelte="true" title="Scene.svelte"><div class='code-title'>Scene.svelte</div><div class="language-id">svelte</div><div class='code-container'><code><div class='line'>&lt;script&gt;</div><div class='line'>  import Decals from './Decals.svelte'</div><div class='line'>  let decalRef</div><div class='line'></div><div class='line'>  function click(e) &#123;</div><div class='line'>    const intersection = checkIntersection(e.clientX, e.clientY)</div><div class='line'>    if (intersection.intersects) &#123;</div><div class='line'>      const rotation = new THREE.Euler().copy(mouseHelper.rotation)</div><div class='line'>      decalRef.addDecal(new THREE.Vector3().copy(intersection.point), rotation)</div><div class='line'>    &#125; else console.log('no intersect')</div><div class='line'>  &#125;</div><div class='line'>&lt;/script&gt;</div><div class='line'></div><div class='line'>&lt;svelte:window on:click=&#123;click&#125; on:mousemove=&#123;mouseMove&#125; /&gt;</div><div class='line'>&lt;Decals bind:this=&#123;decalRef&#125; &#123;mesh&#125; /&gt;</div></code></div></pre><!-- HTML_TAG_END --><p>With this we add a new component and tell it to add a decal whenever we click on the target.</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" svelte="true" title="Decals.svelte"><div class='code-title'>Decals.svelte</div><div class="language-id">svelte</div><div class='code-container'><code><div class='line'>&lt;script&gt;</div><div class='line'>  import &#123; DecalGeometry &#125; from 'three/addons/geometries/DecalGeometry.js'</div><div class='line'>  import &#123; T &#125; from '@threlte/core'</div><div class='line'>  import * as THREE from 'three'</div><div class='line'></div><div class='line'>  export let mesh</div><div class='line'></div><div class='line'>  const textureLoader = new THREE.TextureLoader()</div><div class='line'>  const decal = textureLoader.load('/favicon.png')</div><div class='line'>  const decalMat = new THREE.MeshPhongMaterial(&#123;</div><div class='line'>    depthWrite: false,</div><div class='line'>    polygonOffset: true, //Without this the polygons are overlapping the target and causing artifacts</div><div class='line'>    polygonOffsetFactor: -4,</div><div class='line'>    map: decal,</div><div class='line'>    transparent: true //if the decal is a png with transparency this is needed</div><div class='line'>  &#125;)</div><div class='line'></div><div class='line'>  export function addDecal(position, rotation) &#123;</div><div class='line'>    const material = decalMat.clone()</div><div class='line'>    const size = new THREE.Vector3(1, 1, 1)</div><div class='line'>    const m = new THREE.Mesh(new DecalGeometry(mesh, position, rotation, size), material)</div><div class='line'>    m.renderOrder = decals.length // give decals a fixed render order</div><div class='line'></div><div class='line'>    decals = decals.concat([m])</div><div class='line'>  &#125;</div><div class='line'>  let decals = []</div><div class='line'>&lt;/script&gt;</div><div class='line'></div><div class='line'>&#123;#each decals as decal&#125;</div><div class='line'>  &lt;T.Mesh is=&#123;decal&#125; /&gt;</div><div class='line'>&#123;/each&#125;</div></code></div></pre><!-- HTML_TAG_END --><p>This code is pretty short, and to the point. Most of the heavy lifting is done by the <code>DecalGeometry</code> object.</p><h2 id="result"><a href="#result">Result</a></h2><p>Assuming that everything has been copy pasted correctly it should look something like this.</p><p><picture><source srcset="/_app/immutable/assets/final-46c02292.avif 736w" type="image/avif">  <img alt="final" class="rounded-lg my-2" loading="lazy" decoding="async"></picture></p><p>If not, I suggest having a look at my source code on <a href="https://github.com/munHunger/decals" rel="nofollow noopener noreferrer external" target="_blank">github</a>.</p><p>Lastly if you want to see it in action you can try it out <a href="https://munhunger.github.io/decals/" rel="nofollow noopener noreferrer external" target="_blank">here</a></p>]]>
    </content>
    <category term="three js" scheme="https://blog.wunderdev.com/?tags=three%20js" />
    <category term="tutorial" scheme="https://blog.wunderdev.com/?tags=tutorial" />
  </entry>
  <entry>
    <title type="html"><![CDATA[Blurring the lines between IaC and C with wing]]></title>
    <link href="https://blog.wunderdev.com/blog/wing/1" />
    <id>https://blog.wunderdev.com/blog/wing/1</id>
    <published>2023-09-15T00:00:00.000Z</published>
    <updated>2024-12-16T12:47:01.482Z</updated>
    <summary type="html"><![CDATA[Winglang removes the current idea of separating infrastructure as code and normal code. And it solves the current pain points of cloud development by running everything locally.]]></summary>
    <content type="html">
      <![CDATA[<p>Do you remember the last time you saw some piece of tech that made you go “Wow! Why haven’t we seen more of this already?“.</p><p>Besides the topic of this blog post, the last time for me was <a href="https://svelte.dev/" rel="nofollow noopener noreferrer external" target="_blank">svelte</a>. It made me think “why are we shipping massive frameworks to browser”. For those who don’t know svelte is a compiler that takes your frontend code and bundles it into a page. And the code you write feels like it is written in a normal framework, but instead of shipping hundreds of kB or mB of just framework code, Svelte bundles into a small package with only the code needed to run your website.</p><p>This post isn’t about svelte though, it is about the latest piece of tech that blew my mind away.Namely <a href="https://www.winglang.io/" rel="nofollow noopener noreferrer external" target="_blank">wing</a>.</p><h2 id="wing"><a href="#wing">Wing</a></h2><p>So what is it about wing that made me go “Wow!“?</p><p>Wing is the first language I’ve seen that covers the full stack and is truly cloud native.What I mean by that is that the code you write can be compiled into a terraform script that deploys your app to AWS (or other clouds). This sounds like such a minor thing, because writing terraform scripts isn’t that painful and separating infrastructure as code from normal code doesn’t sound that bad. But what wing does, which I find amazing is that it removes the concept of IaC being different from just code.</p><h3 id="hello-world"><a href="#hello-world">Hello World</a></h3><p>It might be easiest if I show a basic example of how it works.</p><!-- HTML_TAG_START --><pre class="shiki material-default"><div class='code-container'><code><div class='line'>bring cloud;</div><div class='line'></div><div class='line'>let counter = new cloud.Counter();</div><div class='line'>let api = new cloud.Api();</div><div class='line'>api.get("/hello", inflight (): cloud.ApiResponse =&gt; &#123;</div><div class='line'>    counter.inc();</div><div class='line'>    return cloud.ApiResponse &#123;</div><div class='line'>        status: 200,</div><div class='line'>        body: "world $&#123;counter.peek()&#125;"</div><div class='line'>    &#125;;</div><div class='line'>&#125;);</div></code></div></pre><!-- HTML_TAG_END --><p>This hello world example will create an api gateway with a lambda that responds to <code>/hello</code>.For each request it will update a counter stored in dynamoDB and respond with <code>world x</code>, where x is the current value of the counter.</p><p>This example in itself should be enough to convince you that wing is a pretty cool piece of tech to consider.But lets take it a few steps further!</p><p>By running this command</p><!-- HTML_TAG_START --><pre class="shiki material-default" bash="true"><div class="language-id">bash</div><div class='code-container'><code><div class='line'>wing it main.w</div></code></div></pre><!-- HTML_TAG_END --><p>you start a local simulation of your code (think <a href="https://localstack.cloud/" rel="nofollow noopener noreferrer external" target="_blank">localstack</a>, but slimmer).And you get a pretty nice UI that shows you how everything is connected, and you can click around to see your state, and execute parts of your code.<picture><source srcset="/_app/immutable/assets/hello_world-8ebd07fd.avif 736w" type="image/avif">  <img alt="hello world" class="rounded-lg my-2" loading="lazy" decoding="async"></picture></p><p>Running things locally is in my opinion one of the biggest downsides of going cloud… or rather, the fact that it is difficult to do so.At my previous employments I’ve used localstack quite a lot, because without it I have no idea how my code will react before I release it. And hopefully I haven’t released it to prod yet.</p><p>So being able to click around like this is a huge win in my books!</p><p>But why stop with being able to simulate your local cloud?Let us add a test and you will see how powerful this simulation is</p><!-- HTML_TAG_START --><pre class="shiki material-default"><div class='code-container'><code><div class='line'>bring cloud;</div><div class='line'>bring http;</div><div class='line'></div><div class='line'>let counter = new cloud.Counter();</div><div class='line'></div><div class='line'>let api = new cloud.Api();</div><div class='line'>api.get("/hello", inflight (): cloud.ApiResponse =&gt; &#123;</div><div class='line'>    counter.inc();</div><div class='line'>    return cloud.ApiResponse &#123;</div><div class='line'>        status: 200,</div><div class='line'>        body: "world $&#123;counter.peek()&#125;"</div><div class='line'>    &#125;;</div><div class='line'>&#125;);</div><div class='line'></div><div class='line'>test "counter increments per request" &#123;</div><div class='line'>    assert(http.get(api.url + "/hello").body == "world 1");</div><div class='line'>    assert(http.get(api.url + "/hello").body == "world 2");</div><div class='line'>    assert(http.get(api.url + "/hello").body == "world 3");</div><div class='line'>&#125;</div></code></div></pre><!-- HTML_TAG_END --><p><picture><source srcset="/_app/immutable/assets/test-d61782b1.avif 736w" type="image/avif">  <img alt="test" class="rounded-lg my-2" loading="lazy" decoding="async"></picture></p><p>Pretty simple, right?And this tests that if you send an HTTP request to <code>/hello</code> a lambda function will run and update dynamodb with each request.That is not something I would consider easy to test normally, unless you first deploy it to aws or localstack and then run an integration test against that.</p><p>I am also a massive fan of having the test code in the same file as the real code. It elevates them to first class citizens, which they rightfully are.</p><h3 id="adding-complexity"><a href="#adding-complexity">Adding complexity</a></h3><p>Let us assume that doing updates to the counter is pretty slow, and we want to do that asynchronously.</p><!-- HTML_TAG_START --><pre class="shiki material-default"><div class='code-container'><code><div class='line'>bring cloud;</div><div class='line'></div><div class='line'>let counter = new cloud.Counter();</div><div class='line'>let queue = new cloud.Queue();</div><div class='line'>let api = new cloud.Api();</div><div class='line'></div><div class='line'>queue.setConsumer(inflight () =&gt; &#123;</div><div class='line'>    counter.inc();</div><div class='line'>&#125;);</div><div class='line'></div><div class='line'>api.get("/hello", inflight (): cloud.ApiResponse =&gt; &#123;</div><div class='line'>    queue.push();</div><div class='line'>    return cloud.ApiResponse &#123;</div><div class='line'>        status: 200,</div><div class='line'>        body: "world $&#123;counter.peek()&#125;"</div><div class='line'>    &#125;;</div><div class='line'>&#125;);</div></code></div></pre><!-- HTML_TAG_END --><p><picture><source srcset="/_app/immutable/assets/queue-28d63faf.avif 736w" type="image/avif">  <img alt="queue" class="rounded-lg my-2" loading="lazy" decoding="async"></picture>Can you imagine doing this with javascript and terraform?It is pretty insane how much power you are getting out of so little code.</p><h3 id="inflight-vs-preflight"><a href="#inflight-vs-preflight">Inflight vs preflight</a></h3><p>Wing manages to be this cool by utilizing what they call <code>inflight</code> and <code>preflight</code> code.You might have notices the <code>inflight</code> tag in the examples above. It indicates that the code should be deployed and run in the cloud. Everything else is only run during setup.But the fact that you can access preflight resources from an inflight context makes the code feel pretty seamless and you don’t realize that you are writing IaC.</p><h2 id="downsides"><a href="#downsides">Downsides</a></h2><p>No language is perfect.Winglang is no exception to that rule. It is very much still in beta, and it shows. It feels like it has a lot and what it has shows a lot of potential, but it does not feel like a language that is mature enough to be in production yet.</p><p>One example for that is this:at time of writing they support noSQL like dynamoDB via the <em>“external libraries”</em>. Great! I didn’t expect seeing a relational database this early in development. But this library is really limited. You can do insert, delete, and get just like expected, but if you don’t know the key, you are stuck with a full scan as the <code>.list()</code> method takes no argument you are getting the entire table dumped.</p><p>I should be clear and say that this might be something that could be solved at any time as the language is undergoing some pretty heavy development.</p><h2 id="conclusion"><a href="#conclusion">Conclusion</a></h2><p>I for one am hoping that they continue with the winglang project, as it shows so much potential. And I will be keeping my eyes open in hopes of seeing the first 1.0 version.</p>]]>
    </content>
    <category term="wing" scheme="https://blog.wunderdev.com/?tags=wing" />
    <category term="language design" scheme="https://blog.wunderdev.com/?tags=language%20design" />
  </entry>
  <entry>
    <title type="html"><![CDATA[Fever dreams]]></title>
    <link href="https://blog.wunderdev.com/blog/kanlang/4" />
    <id>https://blog.wunderdev.com/blog/kanlang/4</id>
    <published>2023-09-14T00:00:00.000Z</published>
    <updated>2024-12-16T12:47:01.438Z</updated>
    <summary type="html"><![CDATA[Slacklining the line between genius and insanity]]></summary>
    <content type="html">
      <![CDATA[<p>I feel like I just woke up from a fever dream. The language I have been working on the past month or so is now in a state where you theoretically could use it to write code and solve problems.And for the first time since starting this project I am really looking at what I’ve done.I sort of wish that I could write how it became a good language that I hope someone would want to use, but in reality that was never the intention. It didn’t turn out to be a good language, but I am surprise that it is not <em>just</em> a steaming pile of shit.</p><h2 id="kanlang"><a href="#kanlang">Kanlang</a></h2><p>So for those that haven’t followed this blog, I am talking about my programming language <a href="https://kanlang.wunderdev.com" rel="nofollow noopener noreferrer external" target="_blank">Kanlang</a>.It is a language where function names and direct function invocations plain and simple aren’t part of the language.I don’t what to repeat myself to much so either check out the language <a href="https://kanlang.wunderdev.com" rel="nofollow noopener noreferrer external" target="_blank">here</a> or read more in my <a href="https://blog.wunderdev.com/?tags=kanlang" rel="nofollow noopener noreferrer external" target="_blank">previous posts</a></p><h2 id="takeaways"><a href="#takeaways">Takeaways</a></h2><p>OK, so what have I learnt from this journey?</p><p>I learnt that you should implement your own parser. I started out using <a href="https://nearley.js.org/" rel="nofollow noopener noreferrer external" target="_blank">nearley</a> and while it is a great tool, it fell a bit short when the complexity grew. After replacing it with my own implementation of <a href="https://en.wikipedia.org/wiki/Earley_parser" rel="nofollow noopener noreferrer external" target="_blank">earley parser</a> everything started flowing so much smoother. And in the end I got a much better understanding of how a parser works. Worth noting that if you are creating a “real language” unlike the POC that I created, you might want to consider something other than earley as it isn’t the fastest algorithm out there (but it handles grammar ambiguity and both left and right recursion).</p><p>Going slow is going fast. More than once did I take what looked like minor shortcuts to solve the immediate problem I was facing. It made the project move forwards and I was pretty happy with that, but more often than not did it explode in my face later on, and it because a mess of tangled code. I think it would have been a lot faster to simply take a breath and take my time with each and every problem, no matter how small.</p><p>However my most important takeaway is that I am apparently really into this stuff. Writing this compiler has been a ton of fun and now all of a sudden I’ve added a hammer to my toolbelt and everything is starting to look an awful lot like a nail.</p>]]>
    </content>
    <category term="kanlang" scheme="https://blog.wunderdev.com/?tags=kanlang" />
    <category term="language design" scheme="https://blog.wunderdev.com/?tags=language%20design" />
    <category term="compiler" scheme="https://blog.wunderdev.com/?tags=compiler" />
  </entry>
  <entry>
    <title type="html"><![CDATA[Keyboard slavery]]></title>
    <link href="https://blog.wunderdev.com/blog/kanlang/3" />
    <id>https://blog.wunderdev.com/blog/kanlang/3</id>
    <published>2023-08-17T00:00:00.000Z</published>
    <updated>2024-12-16T12:47:01.426Z</updated>
    <summary type="html"><![CDATA[Modern typescript forces us into slavery under the rule of compilers and linters]]></summary>
    <content type="html">
      <![CDATA[<p>Variable assignment syntax seem to differ quite a bit when looking at different programming languages.</p><p>For example in pascal it is pretty succinct with the assignment operator <code>:=</code> and the reassignment operator <code>=</code>.</p><!-- HTML_TAG_START --><pre class="shiki material-default" pascal="true"><div class="language-id">pascal</div><div class='code-container'><code><div class='line'>a :=3</div><div class='line'>a = 2</div></code></div></pre><!-- HTML_TAG_END --><p>In older versions of java you had to write out your type in front of the variable name to create it.</p><!-- HTML_TAG_START --><pre class="shiki material-default" java="true"><div class="language-id">java</div><div class='code-container'><code><div class='line'>int foo = 3</div></code></div></pre><!-- HTML_TAG_END --><p>I don’t think this makes much sense, as the right hand side is of course already a type, so the type of foo can be inferred from the right side of the operator. More than that it is of course also enforced, so this is not semantically valid.</p><!-- HTML_TAG_START --><pre class="shiki material-default" java="true"><div class="language-id">java</div><div class='code-container'><code><div class='line'>int foo = "bar"</div></code></div></pre><!-- HTML_TAG_END --><p>The modern java developer will be quick to point out that this is a thing of the past as the <code>var</code> keyword was introduced in java 10</p><!-- HTML_TAG_START --><pre class="shiki material-default" java="true"><div class="language-id">java</div><div class='code-container'><code><div class='line'>var foo = "bar"</div></code></div></pre><!-- HTML_TAG_END --><p>Which allows the type of foo to resolve to the type of the right hand side of the assignment.I think this is great, but my experience is that it hasn’t been met with the reception it deserves, and instead the adoption has been somewhat hesitant.</p><h1 id="on-to-javascript"><a href="#on-to-javascript">On to javascript</a></h1><p>JS is a beast when it comes to this.It has no less than 3 assignment keywords, <code>var</code>, <code>let</code>, and <code>const</code>.</p><p><code>let</code> and <code>const</code> are similar to each other. The only difference is that <code>const</code>, like you could guess is a constant and thus can’t be reassigned. Note here that it does not mean it is immutable, i.e. this is valid.</p><!-- HTML_TAG_START --><pre class="shiki material-default" js="true"><div class="language-id">js</div><div class='code-container'><code><div class='line'>const a = &#123;foo: "bar"&#125;</div><div class='line'>a.foo = "hello"</div></code></div></pre><!-- HTML_TAG_END --><p>On a slight side note this can be done in java as well with the keyword <code>final</code></p><!-- HTML_TAG_START --><pre class="shiki material-default" java="true"><div class="language-id">java</div><div class='code-container'><code><div class='line'>final int foo = 3</div></code></div></pre><!-- HTML_TAG_END --><p>And in fact this <code>final</code> is inferred by the compiler if you don’t change the value and don’t add <code>final</code></p><p><code>var</code> is a bit of a game changer in the fact that it escapes the current scope.For instance this piece of code</p><!-- HTML_TAG_START --><pre class="shiki material-default" js="true"><div class="language-id">js</div><div class='code-container'><code><div class='line'>try &#123;</div><div class='line'>  var foo = "bar"</div><div class='line'>&#125; catch (e) &#123;&#125;</div><div class='line'>console.log(foo)</div></code></div></pre><!-- HTML_TAG_END --><p>Is fully valid and prints <code>bar</code>. You can imagine it becomes more interesting if the right hand side could throw errors.If you aren’t using <code>var</code> it would look like this</p><!-- HTML_TAG_START --><pre class="shiki material-default" js="true"><div class="language-id">js</div><div class='code-container'><code><div class='line'>let foo;</div><div class='line'>try &#123;</div><div class='line'>foo = "bar"</div><div class='line'>&#125; catch(e) &#123;&#125;</div><div class='line'>console.log(foo)</div></code></div></pre><!-- HTML_TAG_END --><p>To me I would prefer the <code>var</code> code example.But here is where it becomes an issue.</p><p>Standard lint rules for javascript (and typescript) blocks all usage of <code>var</code>, which is crippling the language.</p><p>On top of that the standard rules tell you that you <em>have</em> to use <code>const</code> if you variable is not changing, something that is inferred by the typescript/javascript compiler/runtime.</p><h2 id="where-am-i-getting-with-this"><a href="#where-am-i-getting-with-this">where am I getting with this</a></h2><p>OK, so there are different ways of assigning variables, what is my point?The point I am trying to make is that variable assignments in programming should be the same no matter how it is used, or the diff should provide some additional layer of control.</p><!-- HTML_TAG_START --><pre class="shiki material-default" java="true"><div class="language-id">java</div><div class='code-container'><code><div class='line'>final int a = 3</div></code></div></pre><!-- HTML_TAG_END --><p>the <code>final</code> part adds a layer of control meaning it shouldn’t be changed. But nothing is forcing it to be there.</p><p>The <code>int</code> part however is not providing control and it is just keyboard slavery. I, as a developer <strong>have</strong> to write <code>int</code> (prior to java 10).</p><p>In typescript/javascript the compiler isn’t forcing me to do anything but the lint rules that are everywhere and considered best practice will make me a slave under its’ rules.I have to write either <code>let</code> or <code>const</code> and I have no choice in which one I pick, it is predetermined by the linter.</p><h2 id="my-own-design"><a href="#my-own-design">My own design</a></h2><p>In the language i am writing (<code>kanlang</code>) I will use the same type as pascal, i.e. <code>:=</code> for the simple reason that it is short and to the point. The type is inferred from the right hand side, and there is no keyboard slavery.For solving the “issue” with having constant variables, I am considering something along the lines of <code>!:=</code> to indicate that we are assigning and we are not allowed to change it</p><h2 id="conclusion"><a href="#conclusion">Conclusion</a></h2><p>Us developers should be free to make our own decisions of the code, and the compiler should feel free to add optimisations such as implicit <code>final</code> where it doesn’t change any functionality but improves the performance.</p>]]>
    </content>
    <category term="kanlang" scheme="https://blog.wunderdev.com/?tags=kanlang" />
    <category term="language design" scheme="https://blog.wunderdev.com/?tags=language%20design" />
  </entry>
  <entry>
    <title type="html"><![CDATA[Rigid and unique]]></title>
    <link href="https://blog.wunderdev.com/blog/kanlang/2" />
    <id>https://blog.wunderdev.com/blog/kanlang/2</id>
    <published>2023-04-18T00:00:00.000Z</published>
    <updated>2024-12-16T12:47:01.426Z</updated>
    <summary type="html"><![CDATA[A broken language with weird type systems that you will love to hate]]></summary>
    <content type="html">
      <![CDATA[<p>Have you spent sleepless nights endlessly searching for the next big thing in programming languages?Perhaps a language that is barely working? Or some that, if it did work, was quirky enough to deter anyone developer in their right mind?</p><p>Well look no further for I have just the thing for you!</p><h1 id="kanlang"><a href="#kanlang">Kanlang</a></h1><p>A while back I wrote a <a href="/blog/kanlang/1/">post</a> about a conceptual language that I wanted to create, and today I have reached a milestone!The language is barely working, but it is almost a proof of concept.</p><p>So what is it all about?</p><p>Kanlang is/will be a Object oriented language with a rigid typesystem, where no two functions can have the same return type.</p><p>That is the core point of Kanlang, and from that one can create some pretty weird systems. For example, if I create a variable of type <code>A</code> any transformation of that variable to, for instance type <code>B</code> can be done implicity without calling any functions.</p><p>Let me give you an example:</p><!-- HTML_TAG_START --><pre class="shiki material-default" js="true"><div class="language-id">js</div><div class='code-container'><code><div class='line'>fn celsiusToFahrenheit(celsius Celsius alias num) Fahrenheit alias num &#123;</div><div class='line'>    num fahrenheit = celsius * 9 / 5 + 32</div><div class='line'>    return fahrenheit</div><div class='line'>&#125;</div><div class='line'>Celsius c = 100</div><div class='line'>Fahrenheit f</div></code></div></pre><!-- HTML_TAG_END --><p>We have a <code>Celsius</code> type in our scope and we have a function that converts from <code>Celsius</code> to <code>Fahrenheit</code>, so we don’t need to explicitly call said function. It is enough for us to declare that we want <code>Fahrenheit</code> and the language will use whatever we have in our scope to make that happen.</p><p>Note that there shouldn’t be anything stopping you from directly calling the <code>celsiusToFahrenheit</code> function directly if you want… except that it is not yet implemented</p><h1 id="proof-of-concept"><a href="#proof-of-concept">Proof of concept</a></h1><p>So Kanlang is not really done.I think calling it a proof of concept is overselling it a bit as well.Maybe we can go for a pre-proof of concept?</p><p>I think that the idea of this language is kind of fun and interesting.And there is a saying that constraints are good for innovation and creativity, so I have a hope that the constraints of Kanlang will lead to something.Of course that something will not be production, but maybe another language somewhere down the lines.</p><h1 id="current-and-future"><a href="#current-and-future">Current and future</a></h1><p>Currently there are tons of things not implemented.To name a few:</p><ul><li>Its’ typesystem is not that rigid yet</li><li>It only really supports numbers</li><li>It doesn’t support direct function calls</li></ul><p>But it is available in an <a href="https://kanlang.wunderdev.com/" rel="nofollow noopener noreferrer external" target="_blank">online editor</a>.It doesn’t run anything but it shows the javascript that it is compiled down to.</p><h2 id="advent-of-code"><a href="#advent-of-code">Advent of code</a></h2><p>I have so far had a lot of fun creating this language, so I will not stop here.My plan is to continue to develop it into something that actually works.And to make things a bit more fun for me I will start doing <a href="https://adventofcode.com/" rel="nofollow noopener noreferrer external" target="_blank">advent of code</a> challenges with the language, and fix whatever feature I need in order to pass each challenge.</p><p>I am guessing it is going to take me more than a month to do that though, so wish me luck!</p><p>Oh, and if you want to have a look at the internals of it, then check out the <a href="https://github.com/munHunger/kanlang" rel="nofollow noopener noreferrer external" target="_blank">github repo</a></p>]]>
    </content>
    <category term="kanlang" scheme="https://blog.wunderdev.com/?tags=kanlang" />
    <category term="compiler" scheme="https://blog.wunderdev.com/?tags=compiler" />
  </entry>
  <entry>
    <title type="html"><![CDATA[Why your tech interviews are bad]]></title>
    <link href="https://blog.wunderdev.com/blog/techInterviews/1" />
    <id>https://blog.wunderdev.com/blog/techInterviews/1</id>
    <published>2023-03-23T00:00:00.000Z</published>
    <updated>2023-03-23T00:00:00.000Z</updated>
    <summary type="html"><![CDATA[An outline of why the most common tech interview structures are bad, and what could be done instead]]></summary>
    <content type="html">
      <![CDATA[<p>The title may seem harsh, but I feel strongly that most tech interviews I’ve had and held have been pretty shaky. Let me start by listing some common interview types I think are quite bad, before giving away the golden answer for how to hold a tech interview.</p><h2 id="code-assignment"><a href="#code-assignment">Code assignment</a></h2><p>This type involves giving the applicant an assignment to develop and submit before the interview to ensure they know how to write code. However, it doesn’t give the interviewer much input. Without knowing the conditions the code was written under, how long it took, and how much help the applicant received, it becomes challenging to gauge their skills. </p><p>It should be a starting point for discussions, but it could also be overwhelming depending on the task. </p><p>Some people may even turn down offers that require homework, not because they don’t know how to code, but because of the principle of it.</p><h2 id="lets-just-talk-about-it"><a href="#lets-just-talk-about-it">Let’s just talk about it</a></h2><p>This approach is way to common. You sit down for an hour with a manager, a developer, and the applicant and talk loosely about what they have worked with.And after the hour is up you go with gut and approve or reject them soley because of their confidence, which it usually boils down to.</p><p>The issue with this approach is that it is really hard to gauge what the applicant knows. And I think it is really easy to pass these interviews if you just “sound confident”. On the other side of it, it is really hard as an interviewer to get a feel for how much they actually know.</p><h2 id="create-a-quicksort-algorithm"><a href="#create-a-quicksort-algorithm">Create a quicksort algorithm</a></h2><p>I’ve never been on either side of this type of interview, where you get a code problem to solve live in front of the interviewers.Which is unfortunate, because I would like to try it. That said I can kind of see a flaw in this approach which is that day-to-day work is just so disconnected for these types of tasks.</p><p>My job is basically just pushing data around… with some bells and whistles.I am not doing anything remotely complex enough for me to have to be that strong in algorithmic design. I hope that I am anyways, but as said, I’ve never gotten this type of interview.</p><h1 id="how-to-interview-the-right-way"><a href="#how-to-interview-the-right-way">How to interview the right way</a></h1><p>In my opinion, the most valuable assignment to evaluate someone’s skills is to have them review a piece of code. It doesn’t take more than 30 minutes, and it gives you a pretty good insight into whether they know what they are talking about or not.</p><p>So here is how you do it. You create a piece of code in the “target language” that is roughly 50 lines of code. This could be a class, a UI component, a PHP file, or whatever seems reasonable.Then, start adding errors. Add easy syntax errors, such as not referring to <code>this</code> where needed, or null checking before assignments(when you know it to be null).Add some bad practices, like not doing anything in a <code>catch</code> statement. Add some bigger structural issues, such as <em>“should this UI component really be in charge of HTTP requests, or shouldn’t that go through a service layer?”</em>.</p><p>Hand them this piece of code, and tell them that it is full of errors.Tell them to treat it as a pull request, and ask if they would be happy if it got released to production.Ask them if they would do something differently.Give them ten minutes to think, where you leave the room and grab a coffee.</p><p>When you come back they can give you their findings, and you can discuss what they found. In my experience it is rare for anyone to find all the errors, so you should guide them a bit. For example <em>“what would happen here on line <code>x</code> if <code>y</code>?”</em>. Either they get it at that point or they are completely lost. It becomes a great indicator of whether they know what they are talking about, as you clearly know that a senior developer should have spotted the error straight away, or as a junior dev they should spot it if they are guided a bit towards it.</p><p>I’ve turned down quite a lot of developers that sounded really confident, but could not do an easy code review. There is, of course, the counterargument that they might just be nervous or stressed, but I think that if you can’t handle that, I wouldn’t trust you to help out when there is a fire in production.</p><h1 id="conclusion"><a href="#conclusion">Conclusion</a></h1><p>In conclusion, conducting a code review as part of your interview process can be a valuable tool for evaluating a candidate’s skills and determining if they are the right fit for the job. It allows you to gauge their ability to read code, attention to detail, and understanding of best practices. By following the steps outlined in this post, you can improve your chances of hiring the right developer for your team.</p>]]>
    </content>
    <category term="Interviews" scheme="https://blog.wunderdev.com/?tags=Interviews" />
  </entry>
  <entry>
    <title type="html"><![CDATA[Breaking the mold]]></title>
    <link href="https://blog.wunderdev.com/blog/kanlang/1" />
    <id>https://blog.wunderdev.com/blog/kanlang/1</id>
    <published>2023-03-20T00:00:00.000Z</published>
    <updated>2024-12-16T12:47:01.418Z</updated>
    <summary type="html"><![CDATA[Conceptualizing a new programing language that breaks how we normally think about development]]></summary>
    <content type="html">
      <![CDATA[<p>I want to stop writing normal object oriented code. And I think it is about time that we start rethinking things a little bit when it comes to programing. Now, mind you I am not saying that I will cause the next revolution in IT, but with some luck I might help someone get a much better idea than the one I am having.</p><p>So, what is this revolution I am talking about?Aspect-oriented programming!… I know it is not new, but if you combine it with other paradigms, and drag it to it’s extreme it can become a odd language that someone might enjoy. (emphasis on the “might”)</p><h2 id="introducing-kanlang-the-aspect-oriented-programming-language"><a href="#introducing-kanlang-the-aspect-oriented-programming-language">Introducing Kanlang: The Aspect-Oriented Programming Language</a></h2><p>If you’re a software developer, you’ve probably heard of aspect-oriented programming (AOP). AOP is a programming paradigm that focuses on modularizing cross-cutting concerns, such as logging, caching, and security, into separate units called “aspects.” By separating these concerns from the rest of the code, AOP helps developers write cleaner, more maintainable code.</p><p>Here are some of the key features of Kanlang:</p><h2 id="unique-return-types"><a href="#unique-return-types">Unique Return Types</a></h2><p>In Kanlang, no two functions can have the same return type. This might sound like a limitation at first, but it’s hopefully a powerful feature that helps you write more expressive code. With unique return types, you can see at a glance what each function does and what it returns. And if you ever need to change the behavior of a function, you can do so without worrying about breaking other parts of the code.</p><p>And I kind of hear you saying that it is going to be an issue, because functions need to be able to return the same object.And you are somewhat right. With Kanlang I am planning on adding type alias, meaning two objects can be virtually the same, but named differently. An authentication function could take in a <code>User</code> object, and return a <code>AuthenticatedUser</code> object.</p><h2 id="dependency-injection-at-the-core"><a href="#dependency-injection-at-the-core">Dependency Injection at the Core</a></h2><p>Dependency injection (DI) is a design pattern that makes code more modular and testable by decoupling the components of a system. In Kanlang, DI is not just a design pattern - it’s a core feature of the language. With Kanlang, you can inject variables wherever you need them, even inside code blocks. And because Kanlang uses static typing, the compiler can check that the injected variables are of the correct type.</p><p>It can even go as far as to check the method arguments that the providing function requires.Meaning that inside of a code block you can inject an <code>AuthenticatedUser</code> without explicitly calling the function, assuming that you have a <code>UnathenticatedUser</code> object somewhere in your scope.</p><h2 id="conclusion"><a href="#conclusion">Conclusion</a></h2><p>This is going to be an extremely odd language, that is likely never to see the light of production, but I see it as a interesting thought experiment.</p><p>And to give you a taste of what I am envisioning here is a code snippet.</p><!-- HTML_TAG_START --><pre class="shiki material-default"><div class='code-container'><code><div class='line'>type User &#123;</div><div class='line'>    email: string</div><div class='line'>    password: string</div><div class='line'>&#125;</div><div class='line'></div><div class='line'>fn authUser(User u) AuthenticatedUser alias User &#123;</div><div class='line'>    if u.email == 'abc@email.com' && u.password == 'abc123' &#123;</div><div class='line'>        return u</div><div class='line'>    &#125;</div><div class='line'>    throw 'User can't authenticate'</div><div class='line'>&#125;</div><div class='line'></div><div class='line'>fn welcomeUser(User u) UserGreeting alias string &#123;</div><div class='line'>    *AuthenticatedUser authedUser //Let the star indicate that it is supplied through DI, and it should "resolve" the call to authUser using u</div><div class='line'>    return "hello " + authedUser.email</div><div class='line'>&#125;</div><div class='line'></div><div class='line'>User myUser = &#123;</div><div class='line'>    email: 'abc@email.com',</div><div class='line'>    password: 'abc123'</div><div class='line'>&#125;</div><div class='line'></div><div class='line'>print(*UserGreeting)</div></code></div></pre><!-- HTML_TAG_END -->]]>
    </content>
    <category term="kanlang" scheme="https://blog.wunderdev.com/?tags=kanlang" />
    <category term="compiler" scheme="https://blog.wunderdev.com/?tags=compiler" />
  </entry>
  <entry>
    <title type="html"><![CDATA[Closet serverless]]></title>
    <link href="https://blog.wunderdev.com/blog/knative" />
    <id>https://blog.wunderdev.com/blog/knative</id>
    <published>2023-02-22T00:00:00.000Z</published>
    <updated>2024-12-16T12:47:01.442Z</updated>
    <summary type="html"><![CDATA[Hosting serverless applications outside of cloud on a home server.]]></summary>
    <content type="html">
      <![CDATA[<h1 id="github-pages"><a href="#github-pages">Github pages</a></h1><p>Lately I’ve been using <a href="https://pages.github.com/" rel="nofollow noopener noreferrer external" target="_blank">github pages</a> a lot, and I would recomend anyone who isn’t using it to start using it.It saves you the hassle of having to get hardware to host a server, scaling if you get a lot of users (not that I do), and general avalability which you usually don’t get with your run of the mill at home closet servers.</p><p>In fact I host this blog on GH pages and it is dead simple with a github action.Here is the one I use to push this blog to GH pages</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" yaml="true" title=".github/workflows/main.yaml"><div class='code-title'>.github/workflows/main.yaml</div><div class="language-id">yaml</div><div class='code-container'><code><div class='line'>name: CI</div><div class='line'>on:</div><div class='line'>  push:</div><div class='line'>    branches: [main]</div><div class='line'># Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages</div><div class='line'>permissions:</div><div class='line'>  contents: read</div><div class='line'>  pages: write</div><div class='line'>  id-token: write</div><div class='line'></div><div class='line'># Allow one concurrent deployment</div><div class='line'>concurrency:</div><div class='line'>  group: 'pages'</div><div class='line'>  cancel-in-progress: true</div><div class='line'></div><div class='line'>jobs:</div><div class='line'>  home:</div><div class='line'>    runs-on: ubuntu-latest</div><div class='line'>    environment:</div><div class='line'>      name: github-pages</div><div class='line'>      url: $&#123;&#123; steps.deployment.outputs.page_url &#125;&#125;</div><div class='line'>    steps:</div><div class='line'>      - uses: actions/checkout@v3</div><div class='line'>      - name: Use Node.js 18</div><div class='line'>        uses: actions/setup-node@v2</div><div class='line'>        with:</div><div class='line'>          node-version: 18</div><div class='line'>      - name: npm install and build</div><div class='line'>        run: |</div><div class='line'>          npm install</div><div class='line'>          npm run build</div><div class='line'>      - name: Setup Pages</div><div class='line'>        uses: actions/configure-pages@v2</div><div class='line'>      - name: Upload artifact</div><div class='line'>        uses: actions/upload-pages-artifact@v1</div><div class='line'>        with:</div><div class='line'>          # Upload entire repository</div><div class='line'>          path: './build'</div><div class='line'>      - name: Deploy to GitHub Pages</div><div class='line'>        id: deployment</div><div class='line'>        uses: actions/deploy-pages@v1</div></code></div></pre><!-- HTML_TAG_END --><p>But this post is not really about GH pages, it is rather about what to do when you can’t use it.</p><h1 id="serverless-backends"><a href="#serverless-backends">Serverless backends</a></h1><p>So I was writing a time reporting page, because who doesn’t love to do time reporting in tons of places!The plan was to make it sync with github gists, but the problem I ran into was that my frontend couldn’t read gists without cors issues.The solution to this was to host a server that can proxy the requests to the frontend.</p><p>Now the issue with that is that I have a bad habbit of creating hobby projects that plain and simply wastes resources by being up all the time.It might not be a lot, since a node server with no traffic doesn’t eat a lot of ram and it doesn’t take that much CPU, but it still hurts me a bit to have them just lay there doing nothing.</p><p>What I could do to mitigate this is just do a “regular” kubernetes deployment and scale it down to zero when it is not needed, but I fear that one day when I do need it, and I have no access to my cluster to scale it up.</p><p>The sane approach is to start using AWS lambda, Azure functions or google cloud functions, as they also scale to zero and cost next to nothing, with the addition of being highly available… but that is not the approach I went with. Instead I installed knative in my k8s cluster.</p><h1 id="knative"><a href="#knative">Knative</a></h1><p><a href="https://knative.dev/docs/" rel="nofollow noopener noreferrer external" target="_blank">Knative</a> is a k8s controller which gives you “serverless” properties on your k8s cluster. Of course it is not serverless as you need your own k8s servers, but you get the point.You can map a domain to a docker image and knative will spin that container up for you when needed, and kill it after a set time of inactivity.</p><p>The deployments are quite easy to setup as well, assuming you aren’t doing anything fancy</p><!-- HTML_TAG_START --><pre class="shiki material-default" yaml="true"><div class="language-id">yaml</div><div class='code-container'><code><div class='line'>apiVersion: serving.knative.dev/v1</div><div class='line'>kind: Service</div><div class='line'>metadata:</div><div class='line'>  name: timestat</div><div class='line'>  namespace: timestat</div><div class='line'>spec:</div><div class='line'>  template:</div><div class='line'>    spec:</div><div class='line'>      containers:</div><div class='line'>        - image: ghcr.io/munhunger/timestat/kit:&#123;&#123; .Values.version &#125;&#125;</div><div class='line'>          ports:</div><div class='line'>            - containerPort: 3000</div></code></div></pre><!-- HTML_TAG_END --><p>That piece of config is all that is needed to get a node container available at <a href="https://timestat.home.wunderdev.com/" rel="nofollow noopener noreferrer external" target="_blank">https://timestat.home.wunderdev.com/</a>, which will get get started when needed and killed after in my case 10 minutes of inactivity.</p><h1 id="microk8s"><a href="#microk8s">microk8s</a></h1><p>I was planning on writing this a while ago, but I got into some issues with upgrading my k8s cluster to support a specific version of knative with auto TLS support.And as it was my third time bricking my k8s cluster during the upgrade I started looking elsewhere as there has to be some k8s installer with sensible defaults.That search led me to my new favorite piece of software, namely <a href="https://microk8s.io/" rel="nofollow noopener noreferrer external" target="_blank">microk8s</a>.</p><p>It might not be for production, but for my home setup it is amazing.Dead simple to install, and it has a pretty decent plugin support (knative among others), so you can quickly spin up a node with some cool k8s controllers and get going within an hour!That is pretty mind boggling, as it usually takes me hours if not days to setup a working k8s cluster (I still have issues with the networking layer).</p><p>Just remember if you are using microk8s to create an ingress towards your knative network gateway. That did cause me some headache.</p><!-- HTML_TAG_START --><pre class="shiki material-default" yaml="true"><div class="language-id">yaml</div><div class='code-container'><code><div class='line'>apiVersion: networking.k8s.io/v1</div><div class='line'>kind: Ingress</div><div class='line'>metadata:</div><div class='line'>  name: kourier-ingress</div><div class='line'>  namespace: knative-serving</div><div class='line'>  annotations:</div><div class='line'>    cert-manager.io/cluster-issuer: lets-encrypt</div><div class='line'>spec:</div><div class='line'>  tls:</div><div class='line'>    - hosts:</div><div class='line'>        - '*.home.wunderdev.com'</div><div class='line'>      secretName: home-tls</div><div class='line'>  rules:</div><div class='line'>    - host: '*.home.wunderdev.com'</div><div class='line'>      http:</div><div class='line'>        paths:</div><div class='line'>          - backend:</div><div class='line'>              service:</div><div class='line'>                name: kourier</div><div class='line'>                port:</div><div class='line'>                  number: 80</div><div class='line'>            path: /</div><div class='line'>            pathType: Prefix</div></code></div></pre><!-- HTML_TAG_END --><p>Without it, I had nginx loadbalancer serve all my network request, and it of course had no concept of knative</p>]]>
    </content>
    <category term="k8s" scheme="https://blog.wunderdev.com/?tags=k8s" />
    <category term="knative" scheme="https://blog.wunderdev.com/?tags=knative" />
    <category term="github pages" scheme="https://blog.wunderdev.com/?tags=github%20pages" />
  </entry>
  <entry>
    <title type="html"><![CDATA[wave function collapse]]></title>
    <link href="https://blog.wunderdev.com/blog/waveFunctionCollapse" />
    <id>https://blog.wunderdev.com/blog/waveFunctionCollapse</id>
    <published>2023-02-06T00:00:00.000Z</published>
    <updated>2024-12-16T12:47:01.478Z</updated>
    <summary type="html"><![CDATA[Implementing the wave function collapse and using it to generate a small factory floor]]></summary>
    <content type="html">
      <![CDATA[<p><a href="https://en.wikipedia.org/wiki/Wave_function_collapse" rel="nofollow noopener noreferrer external" target="_blank">Wave function collapse</a> is a phenomena in quantum physics where a particle or photon goes from behaving as wave to having a known position (from superposition to a single eigenstate).That sentence is probably wildly inacurate, but I am not a quantum physicist.So why am I writing about it?</p><p>Well, there is an algorithm that cleverly has the same name as that phenomena. And more likely than not, you have used it before.Imagine solving a completely empty sodoku. When you start out all cells can be all values, but as soon as you enter a value somewhere(at random) the wave collapses and you impose rules on other cells.Following these easy steps you can collapse the entire board into a “valid” state.</p><p>So the “only” thing to wave function collapse is to figure out what those rules are. Like in sodoku where each number can only appear once per row, per column, and per 3x3 square.</p><h1 id="getting--into-it"><a href="#getting--into-it">Getting  into it</a></h1><p>In this blog post we will use wave function collapse to generate a small factory room.And we will start by looking at these four components that I got from <a href="https://www.kenney.nl/assets/conveyor-kit" rel="nofollow noopener noreferrer external" target="_blank">Kenney</a>.</p><div class="h-96"><canvas class="svelte-o3oskp"></canvas></div>An empty floor, a box, a conveyor, and a conveyor starting point<p>We can setup some easy rules for this, for example:</p><ul><li>a box needs to be next to at least one conveyor</li><li>a conveyor can not be horizontally adjacent to a floor</li><li>the conveyor starting point needs to be adjacent to a conveyor</li><li>the floor can go anywhere </li></ul><p>So, lets create a six by six grid to play around with.</p><div class="h-96"><canvas class="svelte-o3oskp"></canvas></div><p>Notice the numbers on top of each cube, they represent the amount of possible components at each square.To begin with everything is valid, but try clicking a center cube and selecting a conveyor.</p><p>You will notice that the numbers update and to the left and right of the conveyor it says <strong>3</strong>, since our rules do not allow us to have a floor next to a conveyor.</p><p>If you keep selecting valid components you will sooner or later see that adjacent cubes gets updated since there is only one valid option.</p><h1 id="automation"><a href="#automation">Automation</a></h1><p>Clicking around in a janky UI like this might be fun and all, but it does get tiresome, so let me introduce a button that just picks a random square and a random valid component for us.</p><div class="h-96"><canvas class="svelte-o3oskp"></canvas></div><button class="swap-on btn btn-block btn-primary text-base font-normal normal-case transition-all duration-200">step</button><p>This is a very simple implementation and you might have noticed that it did not manage to “solve” the scene and ended up with one or more squares where nothing could be placed.Once again it works like sodoku. You do your best and in some scenarios you need to guess and see where that leads you. If it doesn’t work, then you backtrack to the stage where your last guess where… I know that the true sodoku pro solvers don’t do any guessing, so this might just be my way of doing it. In any case this is generally how you do it with wave function collapse as well. If you end up in an unsolvable state you should backtrack a bit and try again.</p><p>We will not go into details on backtracking in this post.</p><p>But I want to show the code of this basic wave function collapse</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" ts="true" title="entities.ts"><div class='code-title'>entities.ts</div><div class="language-id">ts</div><div class='code-container'><code><div class='line'>function checkHorizontal(ctx: &#123; scene: Array&lt;Array&lt;&#123; type: string &#125;&gt;&gt;, x: number, y: number &#125;, type: Array&lt;string&gt;, depth = 99, strict: boolean = false) &#123;</div><div class='line'>    let offset = 1</div><div class='line'>    let count = 0</div><div class='line'></div><div class='line'>    if (!strict)</div><div class='line'>        type.push("interactive") //since this is a wildcard it should be valid (I call an empty square "interactive" in my code for some reason)</div><div class='line'>    while (type.includes(((ctx.scene[ctx.x + offset] || [])[ctx.y] || &#123;&#125;).type) && offset &lt;= depth) &#123;</div><div class='line'>        count++</div><div class='line'>        offset++</div><div class='line'>    &#125;</div><div class='line'>    offset = -1</div><div class='line'>    while (type.includes(((ctx.scene[ctx.x + offset] || [])[ctx.y] || &#123;&#125;).type) && -offset &lt;= depth) &#123;</div><div class='line'>        count++</div><div class='line'>        offset--</div><div class='line'>    &#125;</div><div class='line'>    return count</div><div class='line'>&#125;</div><div class='line'></div><div class='line'>export abstract class Entity &#123;</div><div class='line'>    constructor(public x: number, public y: number) &#123;</div><div class='line'>    &#125;</div><div class='line'>    abstract isInvalid(scene: Array&lt;Array&lt;&#123; type: string &#125;&gt;&gt;): boolean</div><div class='line'>    abstract name: string</div><div class='line'>&#125;</div><div class='line'></div><div class='line'>export class Box extends Entity &#123;</div><div class='line'>    name = 'box'</div><div class='line'>    isInvalid(scene: &#123; type: string; &#125;[][]): boolean &#123;</div><div class='line'>        return checkHorizontal(&#123; scene, x: this.x, y: this.y &#125;, ['conveyor'], 1) &lt; 1</div><div class='line'>    &#125;</div><div class='line'>&#125;</div><div class='line'></div><div class='line'>export class Conveyor extends Entity &#123;</div><div class='line'>    name = 'conveyor'</div><div class='line'>    isInvalid(scene: &#123; type: string; &#125;[][]): boolean &#123;</div><div class='line'>        return checkHorizontal(&#123; scene, x: this.x, y: this.y &#125;, ['box', 'conveyor', 'conveyor-start'], 1) !== 2 ||</div><div class='line'>            (checkHorizontal(&#123; scene, x: this.x, y: this.y &#125;, ['conveyor-start'], 1, true) &gt;= 2)</div><div class='line'>    &#125;</div><div class='line'>&#125;</div><div class='line'></div><div class='line'>export class ConveyorStart extends Entity &#123;</div><div class='line'>    name = 'conveyor-start'</div><div class='line'>    isInvalid(scene: &#123; type: string; &#125;[][]): boolean &#123;</div><div class='line'>        return checkHorizontal(&#123; scene, x: this.x, y: this.y &#125;, ['conveyor'], 1) &lt; 1</div><div class='line'>    &#125;</div><div class='line'>&#125;</div><div class='line'></div><div class='line'>export class Floor extends Entity &#123;</div><div class='line'>    name = 'floor'</div><div class='line'>    isInvalid(scene: &#123; type: string &#125;[][]): boolean &#123;</div><div class='line'>        return false</div><div class='line'>    &#125;</div><div class='line'>&#125; </div></code></div></pre><!-- HTML_TAG_END --><p>As you can see I framed the rules as “what is not valid”. This way I can try to place for example an <code>Conveyor</code> in a square and check if the entire board is valid. If it is not, then that <code>Conveyor</code> cannot be placed in that square.</p><p>Worth noting that nowhere do I state that this is the way to do it, or that this is in any way performant. It is just the first hacky solution that I got working, and we will update these rules further on in this post.</p><p>In a real world scenario you can expand a lot on these rules. For example you can generate a room, and that room would be invalid if it did not have a door, which would force the algorithm to place a door.</p><p>But in this post we will stick to simple adjacency rules.</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" js="true" title="wfc.js"><div class='code-title'>wfc.js</div><div class="language-id">js</div><div class='code-container'><code><div class='line'>function step() &#123;</div><div class='line'>  do &#123;</div><div class='line'>    updateAllowList()</div><div class='line'>  &#125; while (solveForced() &gt; 0)</div><div class='line'>&#125;</div><div class='line'></div><div class='line'>function cloneScene() &#123;</div><div class='line'>  return JSON.parse(JSON.stringify(scene))</div><div class='line'>&#125;</div><div class='line'></div><div class='line'>function updateAllowList() &#123;</div><div class='line'>  scene.forEach((col, x) =&gt;</div><div class='line'>    col.forEach((entity, y) =&gt; &#123;</div><div class='line'>      if (entity.type !== 'interactive') return</div><div class='line'>      const allowList = [new Box(x, y), new Floor(x, y), new Conveyor(x, y), new ConveyorStart(x, y)]</div><div class='line'>        .filter(type =&gt; &#123;</div><div class='line'>          let sceneClone = cloneScene()</div><div class='line'>          sceneClone[x][y].type = type.name</div><div class='line'>          let valid = true</div><div class='line'>          for (let xX = 0; xX &lt; sceneClone.length; xX++) &#123;</div><div class='line'>            for (let yY = 0; yY &lt; sceneClone[xX].length; yY++) &#123;</div><div class='line'>              let active = [new Box(xX, yY), new Floor(xX, yY), new Conveyor(xX, yY), new ConveyorStart(xX, yY)].find(</div><div class='line'>                v =&gt; v.name == sceneClone[xX][yY].type</div><div class='line'>              )</div><div class='line'>              if (active && active.isInvalid(sceneClone)) valid = false</div><div class='line'>            &#125;</div><div class='line'>          &#125;</div><div class='line'>          return valid</div><div class='line'>        &#125;)</div><div class='line'>        .map(v =&gt; v.name)</div><div class='line'>      solver[x][y] = assets.filter(v =&gt; allowList.includes(v))</div><div class='line'>    &#125;)</div><div class='line'>  )</div><div class='line'>&#125;</div><div class='line'></div><div class='line'>function solveForced() &#123;</div><div class='line'>  let solvedCount = 0</div><div class='line'>  solver.forEach((col, x) =&gt;</div><div class='line'>    col.forEach((cell, y) =&gt; &#123;</div><div class='line'>      if (cell.length === 1 && scene[x][y].type == 'interactive') &#123;</div><div class='line'>        if (rules[cell[0]]) &#123;</div><div class='line'>          rules[cell[0]].apply(&#123;&#125;, [&#123; scene, x, y, checkHorizontal, updateAllowList &#125;])</div><div class='line'>        &#125;</div><div class='line'>        scene[x][y].click(&#123; scene, entity: scene[x][y], x, y, asset: cell[0] &#125;)</div><div class='line'>        solvedCount++</div><div class='line'>      &#125;</div><div class='line'>    &#125;)</div><div class='line'>  )</div><div class='line'>  return solvedCount</div><div class='line'>&#125;</div></code></div></pre><!-- HTML_TAG_END --><p>So in essence what we do is, we go through the entire scene and check which entities are valid for each square.If we find one or more places where only one entity is valid, we place it and repeat.</p><h1 id="automating-the-rules"><a href="#automating-the-rules">Automating the rules</a></h1><p>Since the rules we specified are so simple, I think we can automate the creation of it.The way to do it is to take an example hand crafted scene and check what objects are allowed next to eachother.</p><p>It is a bit tedious to build one of these input scenes, so I’ve spared you from having to do it and we will use this scene that I threw together as a baseline</p><div class="h-96"><canvas class="svelte-o3oskp"></canvas></div><p>And for reference this is how I decided to represent it</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" js="true" title="wfc.js"><div class='code-title'>wfc.js</div><div class="language-id">js</div><div class='code-container'><code><div class='line'>//&lt;entity name&gt;:&lt;rotation of the entity in 90 degree increments&gt;</div><div class='line'>export let defaultScene = [</div><div class='line'>  ['wall-corner:0', 'wall:1', 'wall:1', 'door:1', 'wall:1', 'wall:1', 'wall:1'],</div><div class='line'>  ['wall:0', 'floor:0', 'floor:0', 'floor:0', 'floor:0', 'box:0', 'floor:0'],</div><div class='line'>  ['wall:0', 'box:0', 'floor:0', 'floor:0', 'floor:0', 'conveyor-open:0', 'floor:0'],</div><div class='line'>  ['door:0', 'floor:0', 'floor:0', 'floor:0', 'floor:0', 'conveyor:0', 'floor:0'],</div><div class='line'>  ['wall:0', 'floor:0', 'floor:0', 'floor:0', 'floor:0', 'conveyor-cover:0', 'floor:0'],</div><div class='line'>  ['wall:0', 'floor:0', 'floor:0', 'floor:0', 'floor:0', 'conveyor-open:0', 'floor:0'],</div><div class='line'>  ['port:0', 'floor:0', 'conveyor-start:1', 'conveyor:1', 'conveyor-open:1', 'conveyor-open:0', 'floor:0'],</div><div class='line'>  ['wall:0', 'floor:0', 'floor:0', 'floor:0', 'floor:0', 'floor:0', 'floor:0'],</div><div class='line'>  ['wall:0', 'conveyor-start:1', 'conveyor:1', 'conveyor:1', 'conveyor-cover:1', 'conveyor-open:1', 'box:0'],</div><div class='line'>  ['wall:0', 'floor:0', 'floor:0', 'floor:0', 'floor:0', 'floor:0', 'floor:0'],</div><div class='line'>  ['wall-corner:3', 'wall:3', 'wall:3', 'wall:3', 'wall:3', 'wall:3', 'wall:3']</div><div class='line'>]</div></code></div></pre><!-- HTML_TAG_END --><p>So lets’ see what rules we can manage to extract from this scene. We will begin by simply counting how many times entities occur next to each other. For example how often is there a conveyor to the left and right of a box.</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" js="true" title="wfc.js"><div class='code-title'>wfc.js</div><div class="language-id">js</div><div class='code-container'><code><div class='line'>  function unique(value, index, self) &#123;</div><div class='line'>    return self.indexOf(value) === index</div><div class='line'>  &#125;</div><div class='line'>  export let scene = defaultScene.map(v =&gt; v.map(a =&gt; (&#123; type: a.split(':')[0], rot: a.split(':')[1] &#125;)))</div><div class='line'>  let adjacencyCount = &#123;&#125;</div><div class='line'></div><div class='line'>  /**</div><div class='line'>   * Take the default scene and map it into a list of unique assets</div><div class='line'>   * Have each asset represent a list of all other unique assets with a horizontal and vertical count</div><div class='line'>   */</div><div class='line'>  defaultScene</div><div class='line'>    .flat()</div><div class='line'>    .filter(unique)</div><div class='line'>    .forEach(asset =&gt; &#123;</div><div class='line'>      adjacencyCount[asset] = &#123;&#125;</div><div class='line'>      defaultScene</div><div class='line'>        .flat()</div><div class='line'>        .filter(unique)</div><div class='line'>        .forEach(other =&gt; (adjacencyCount[asset][other] = &#123; horizontal: 0, vertical: 0 &#125;))</div><div class='line'>    &#125;)</div><div class='line'></div><div class='line'>  for (let x = 0; x &lt; defaultScene.length; x++) &#123;</div><div class='line'>    for (let y = 0; y &lt; defaultScene[x].length; y++) &#123;</div><div class='line'>      // we will only use manhattan adjacency so diagonals are irrelevant</div><div class='line'>      for (let xX = -1; xX &lt;= 1; xX += 2) &#123;</div><div class='line'>        if (x + xX &gt;= 0 && x + xX &lt; defaultScene.length)</div><div class='line'>          adjacencyCount[defaultScene[x][y]][defaultScene[x + xX][y]].horizontal++</div><div class='line'>      &#125;</div><div class='line'>      for (let yY = -1; yY &lt;= 1; yY += 2) &#123;</div><div class='line'>        if (y + yY &gt;= 0 && y + yY &lt; defaultScene[x].length)</div><div class='line'>          adjacencyCount[defaultScene[x][y]][defaultScene[x][y + yY]].vertical++</div><div class='line'>      &#125;</div><div class='line'>    &#125;</div><div class='line'>  &#125;</div></code></div></pre><!-- HTML_TAG_END --><p>with that code that does not follow industry standards of not having side effects, the <code>adjacencyCount</code> should look something like this</p><!-- HTML_TAG_START --><pre class="shiki material-default" json="true"><div class="language-id">json</div><div class='code-container'><code><div class='line'>&#123;</div><div class='line'>  "wall:1": &#123;</div><div class='line'>    "wall-corner:0": &#123;</div><div class='line'>      "horizontal": 0,</div><div class='line'>      "vertical": 1</div><div class='line'>    &#125;,</div><div class='line'>    "wall:1": &#123;</div><div class='line'>      "horizontal": 0,</div><div class='line'>      "vertical": 6</div><div class='line'>    &#125;,</div><div class='line'>    "door:1": &#123;</div><div class='line'>      "horizontal": 0,</div><div class='line'>      "vertical": 2</div><div class='line'>    &#125;,</div><div class='line'>    "wall:0": &#123;</div><div class='line'>      "horizontal": 0,</div><div class='line'>      "vertical": 0</div><div class='line'>    &#125;,</div><div class='line'>    "floor:0": &#123;</div><div class='line'>      "horizontal": 4,</div><div class='line'>      "vertical": 0</div><div class='line'>    &#125;,</div><div class='line'>    "box:0": &#123;</div><div class='line'>      "horizontal": 1,</div><div class='line'>      "vertical": 0</div><div class='line'>    &#125;,</div><div class='line'>    "conveyor-open:0": &#123;</div><div class='line'>      "horizontal": 0,</div><div class='line'>      "vertical": 0</div><div class='line'>    &#125;</div><div class='line'>  &#125;</div><div class='line'>&#125;</div></code></div></pre><!-- HTML_TAG_END --><p>A bit shortened of course to focus on the important aspects.But that object tells us that there are 6 walls with a rotation value of 1 stacked next to eachother vertically.It also tells us that there is one box in front of a wall.Looking at it the other way around we see that it is not allowed to have a <code>conveyor-open</code> object anywhere near a wall.</p><p>I want to extend it a little bit so that we know more than just “is it valid”, and adding a probablity of how likely it is that a box is in front of a wall.</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" js="true" title="wfc.js"><div class='code-title'>wfc.js</div><div class="language-id">js</div><div class='code-container'><code><div class='line'>  let rules = &#123;&#125;</div><div class='line'>  Object.keys(adjacencyCount).forEach(entityKey =&gt; &#123;</div><div class='line'>    let entity = adjacencyCount[entityKey]</div><div class='line'>    // figure out the statistical probability of there being an x horizontally adjacent</div><div class='line'>    let totalHorizontal = Object.values(entity)</div><div class='line'>      .map(v =&gt; v.horizontal)</div><div class='line'>      .reduce((acc, val) =&gt; acc + val, 0)</div><div class='line'>    let horizontalRules = Object.keys(entity)</div><div class='line'>      .map(v =&gt; (&#123;</div><div class='line'>        entity: v,</div><div class='line'>        odds: entity[v].horizontal / totalHorizontal</div><div class='line'>      &#125;))</div><div class='line'>      .filter(v =&gt; v.odds &gt; 0)</div><div class='line'></div><div class='line'>    let totalVertical = Object.values(entity)</div><div class='line'>      .map(v =&gt; v.vertical)</div><div class='line'>      .reduce((acc, val) =&gt; acc + val, 0)</div><div class='line'>    let verticalRules = Object.keys(entity)</div><div class='line'>      .map(v =&gt; (&#123;</div><div class='line'>        entity: v,</div><div class='line'>        odds: entity[v].vertical / totalVertical</div><div class='line'>      &#125;))</div><div class='line'>      .filter(v =&gt; v.odds &gt; 0)</div><div class='line'></div><div class='line'>    rules[entityKey] = &#123; horizontalRules, verticalRules &#125;</div><div class='line'>  &#125;)</div></code></div></pre><!-- HTML_TAG_END --><p>So now we should have a normalized list of possible entities</p><!-- HTML_TAG_START --><pre class="shiki material-default" json="true"><div class="language-id">json</div><div class='code-container'><code><div class='line'>&#123;</div><div class='line'>  "wall:1": &#123;</div><div class='line'>    "horizontalRules": [</div><div class='line'>      &#123;</div><div class='line'>        "entity": "floor:0",</div><div class='line'>        "odds": 0.8</div><div class='line'>      &#125;,</div><div class='line'>      &#123;</div><div class='line'>        "entity": "box:0",</div><div class='line'>        "odds": 0.2</div><div class='line'>      &#125;</div><div class='line'>    ],</div><div class='line'>    "verticalRules": [</div><div class='line'>      &#123;</div><div class='line'>        "entity": "wall-corner:0",</div><div class='line'>        "odds": 0.1111111111111111</div><div class='line'>      &#125;,</div><div class='line'>      &#123;</div><div class='line'>        "entity": "wall:1",</div><div class='line'>        "odds": 0.6666666666666666</div><div class='line'>      &#125;,</div><div class='line'>      &#123;</div><div class='line'>        "entity": "door:1",</div><div class='line'>        "odds": 0.2222222222222222</div><div class='line'>      &#125;</div><div class='line'>    ]</div><div class='line'>  &#125;</div><div class='line'>&#125;</div></code></div></pre><!-- HTML_TAG_END --><p>With this it is clear that there is a 1 in 5 that there if a box and not a floor in front of a wall.We will not get any rules saying that if there is a box next to a conveyor, there has to be another conveyor next to that conveyor. But I think this should suffice</p><p>Here is the solver I made using the rules extracted from the default scene.</p><div class="h-96"><canvas class="svelte-o3oskp"></canvas></div><button class="swap-on btn btn-block btn-primary text-base font-normal normal-case transition-all duration-200">reset</button><h1 id="results"><a href="#results">Results</a></h1><p>The first solution I got looked like this</p><p><picture><source srcset="/_app/immutable/assets/2023-02-06_10-06-d0227c70.avif 736w" type="image/avif">  <img alt="conveyors conveyors and conveyors" class="rounded-lg my-2" loading="lazy" decoding="async"></picture></p><p>Which was 100% user error my part, but I found it pretty funny. It also gave me the idea to bootstrap the solver by placing a corner wall at the corner of the scene.This forced the solver to add some walls, and from time to time it added a <strong>lot</strong> of walls</p><p>But in general the result looked acceptable, albeit far from great<picture><source srcset="/_app/immutable/assets/2023-02-06_10-24-f11cbd2f.avif 736w" type="image/avif">  <img alt="random conveyors" class="rounded-lg my-2" loading="lazy" decoding="async"></picture><picture><source srcset="/_app/immutable/assets/2023-02-06_10-32-636933ea.avif 736w" type="image/avif">  <img alt="acceptable result" class="rounded-lg my-2" loading="lazy" decoding="async"></picture></p><p>In all honesty I did not expect it to be this far from perfect, but from time to time it produces an acceptable result.And to be fair, it makes perfect sense that it looks like crap, only looking at the immediately adjacent entites will result in a pretty limited ruleset.</p><p>If I were to expand on this I would force it to encase the scene in walls, by making a rule that checks the perimiter of the scene and making it invalid if it is not encased.Then I would add rules for minimum amount of objects. For example a room would not be complete without a door, or a box, or a conveyor. Given adjacency rules that would force the door to be placed on a wall and not freestanding in the middle of the room.</p>]]>
    </content>
    <category term="procedural generation" scheme="https://blog.wunderdev.com/?tags=procedural%20generation" />
    <category term="typescript" scheme="https://blog.wunderdev.com/?tags=typescript" />
    <category term="wave function collapse" scheme="https://blog.wunderdev.com/?tags=wave%20function%20collapse" />
    <category term="algorithm" scheme="https://blog.wunderdev.com/?tags=algorithm" />
  </entry>
  <entry>
    <title type="html"><![CDATA[Rolling dice with three js]]></title>
    <link href="https://blog.wunderdev.com/blog/DnD/3" />
    <id>https://blog.wunderdev.com/blog/DnD/3</id>
    <published>2023-01-31T00:00:00.000Z</published>
    <updated>2024-12-16T12:47:01.374Z</updated>
    <summary type="html"><![CDATA[Using a rigid body simulation to roll dice using three js and rapier, wrapped with threlte]]></summary>
    <content type="html">
      <![CDATA[<p>Following up on my last post about a <a href="/blog/DnD/2">dice expression parser</a> I wanted to create a simple 3d dice roller.</p><p>Turns out it was not as simple as I expected, but then again, I have no experience at all of this type of stuff.</p><p>So for anyone else wanting to get into 3d browser stuff, let the blind lead the blind.</p><h1 id="setting-up-three-js"><a href="#setting-up-three-js">Setting up three js</a></h1><p>OK, so <a href="https://threejs.org/" rel="nofollow noopener noreferrer external" target="_blank">three js</a> seems to be some form of standard when it comes to browser 3D stuff. I started using it, but ran into some issues when trying to use physics. Stepping up a level in abstraction I found <a href="https://threlte.xyz/" rel="nofollow noopener noreferrer external" target="_blank">threlte</a> which is a threejs wrapper for svelte. You still have access to everything threejs, but it comes with sensible defaults and it has an easy to use integration with <a href="https://rapier.rs/" rel="nofollow noopener noreferrer external" target="_blank">rapier</a>. With rapier and threlte, physics was a lot easier than running with threejs and <a href="https://github.com/kripken/ammo.js/" rel="nofollow noopener noreferrer external" target="_blank">ammo</a> or <a href="https://github.com/pmndrs/cannon-es" rel="nofollow noopener noreferrer external" target="_blank">cannon-es</a></p><p>Now that we know what to use and what abstraction level to be at, lets get started and create a basic scene. I think it makes sense to steal from the best, so following the getting <a href="https://threlte.xyz/getting-started#first-scene" rel="nofollow noopener noreferrer external" target="_blank">started guide at threlte</a>, and mofifying it slightly we get something like this</p><div class="h-96"><canvas class="svelte-o3oskp"></canvas></div><!-- HTML_TAG_START --><pre class="shiki material-default with-title" html="true" title="Canvas.svelte"><div class='code-title'>Canvas.svelte</div><div class="language-id">html</div><div class='code-container'><code><div class='line'>&lt;script&gt;</div><div class='line'>  import &#123; Canvas &#125; from '@threlte/core'</div><div class='line'>&lt;/script&gt;</div><div class='line'></div><div class='line'>&lt;div class="h-96"&gt;</div><div class='line'>  &lt;Canvas&gt;</div><div class='line'>    &lt;slot /&gt;</div><div class='line'>  &lt;/Canvas&gt;</div><div class='line'>&lt;/div&gt;</div></code></div></pre><!-- HTML_TAG_END --><!-- HTML_TAG_START --><pre class="shiki material-default with-title" html="true" title="diceRoller.svelte"><div class='code-title'>diceRoller.svelte</div><div class="language-id">html</div><div class='code-container'><code><div class='line'>&lt;script&gt;</div><div class='line'>  import &#123; OrbitControls, T &#125; from '@threlte/core'</div><div class='line'>  import &#123; degToRad &#125; from 'three/src/math/MathUtils'</div><div class='line'>&lt;/script&gt;</div><div class='line'></div><div class='line'>&lt;T.PerspectiveCamera makeDefault position=&#123;[0, 10, 10]&#125; fov=&#123;24&#125;&gt;</div><div class='line'>  &lt;OrbitControls maxPolarAngle=&#123;degToRad(80)&#125; enableZoom=&#123;false&#125; target=&#123;&#123; y: 0.5 &#125;&#125; /&gt;</div><div class='line'>&lt;/T.PerspectiveCamera&gt;</div><div class='line'></div><div class='line'>&lt;T.DirectionalLight castShadow position=&#123;[3, 10, 10]&#125; /&gt;</div><div class='line'>&lt;T.DirectionalLight position=&#123;[-3, 10, -10]&#125; intensity=&#123;0.2&#125; /&gt;</div><div class='line'>&lt;T.AmbientLight intensity=&#123;0.2&#125; /&gt;</div><div class='line'></div><div class='line'>&lt;T.Group&gt;</div><div class='line'>  &lt;T.Mesh position.y=&#123;0.5&#125; castShadow let:ref&gt;</div><div class='line'>    &lt;T.BoxGeometry /&gt;</div><div class='line'>    &lt;T.MeshStandardMaterial color="#333333" /&gt;</div><div class='line'>  &lt;/T.Mesh&gt;</div><div class='line'>&lt;/T.Group&gt;</div><div class='line'></div><div class='line'>&lt;T.Mesh receiveShadow rotation.x=&#123;degToRad(-90)&#125;&gt;</div><div class='line'>  &lt;T.CircleGeometry args=&#123;[3, 72]&#125; /&gt;</div><div class='line'>  &lt;T.MeshStandardMaterial color="white" /&gt;</div><div class='line'>&lt;/T.Mesh&gt;</div></code></div></pre><!-- HTML_TAG_END --><p>Note that we are using a separate component just for the canvas.The reason for this is so that we later on can use the hook <a href="https://threlte.xyz/core/use-threlte" rel="nofollow noopener noreferrer external" target="_blank"><code>useThrelte</code></a> that gives us access to the threejs scene graph. It will be more obvious later on why this is needed.</p><h1 id="making-it-look-like-a-dice"><a href="#making-it-look-like-a-dice">Making it look like a dice</a></h1><p>I want to focus this blog on only one dice, and I don’t want to make it easy. So lets focus on a twenty sided dice.</p><p>There might a “pre-made” geometry for it, but I’ve exported a one segment ico sphere from <a href="https://www.blender.org/" rel="nofollow noopener noreferrer external" target="_blank">blender</a>.If you want to follow along you can download it <a href="/assets/d20.obj">here</a>.</p><p>So now lets load the dice.</p><div class="h-96"><canvas class="svelte-o3oskp"></canvas></div><!-- HTML_TAG_START --><pre class="shiki material-default with-title" html="true" title="diceRoller.svelte"><div class='code-title'>diceRoller.svelte</div><div class="language-id">html</div><div class='code-container'><code><div class='line dim'>&lt;script&gt;</div><div class='line dim'>  import &#123; useLoader, OrbitControls, T, Mesh &#125; from '@threlte/core'</div><div class='line dim'>  import &#123; OBJLoader &#125; from 'three/examples/jsm/loaders/OBJLoader'</div><div class='line dim'>  import &#123; degToRad &#125; from 'three/src/math/MathUtils'</div><div class='line dim'>  import &#123; onMount &#125; from 'svelte'</div><div class='line dim'>  import &#123; CircleGeometry, DoubleSide, MeshStandardMaterial &#125; from 'three'</div><div class='line'></div><div class='line highlight'>  /**</div><div class='line highlight'>   * @type &#123;import("three").BufferGeometry&#125;</div><div class='line highlight'>   */</div><div class='line highlight'>  let d20Mesh</div><div class='line highlight'>  const loader = useLoader(OBJLoader, () =&gt; new OBJLoader())</div><div class='line highlight'>  onMount(() =&gt; &#123;</div><div class='line highlight'>    loader.load('/assets/d20.obj', obj =&gt; &#123;</div><div class='line highlight'>      d20Mesh = obj.children[0].geometry</div><div class='line highlight'>    &#125;)</div><div class='line highlight'>  &#125;)</div><div class='line dim'>&lt;/script&gt;</div><div class='line'></div><div class='line dim'>&lt;T.PerspectiveCamera makeDefault position=&#123;[0, 10, 10]&#125; fov=&#123;24&#125;&gt;</div><div class='line dim'>  &lt;OrbitControls maxPolarAngle=&#123;degToRad(80)&#125; enableZoom=&#123;false&#125; target=&#123;&#123; y: 0.5 &#125;&#125; /&gt;</div><div class='line dim'>&lt;/T.PerspectiveCamera&gt;</div><div class='line'></div><div class='line dim'>&lt;T.DirectionalLight castShadow position=&#123;[3, 10, 10]&#125; /&gt;</div><div class='line dim'>&lt;T.DirectionalLight position=&#123;[-3, 10, -10]&#125; intensity=&#123;0.2&#125; /&gt;</div><div class='line dim'>&lt;T.AmbientLight intensity=&#123;0.2&#125; /&gt;</div><div class='line'></div><div class='line highlight'>&lt;T.Group&gt;</div><div class='line highlight'>  &lt;Mesh</div><div class='line highlight'>    position=&#123;&#123; x: 0, y: 1.5, z: 0 &#125;&#125;</div><div class='line highlight'>    castShadow</div><div class='line highlight'>    geometry=&#123;d20Mesh&#125;</div><div class='line highlight'>    material=&#123;new MeshStandardMaterial(&#123; color: '#333333', side: DoubleSide &#125;)&#125; /&gt;</div><div class='line highlight'>&lt;/T.Group&gt;</div><div class='line'></div><div class='line highlight'>&lt;Mesh</div><div class='line highlight'>  receiveShadow</div><div class='line highlight'>  interactive</div><div class='line highlight'>  rotation=&#123;&#123; x: degToRad(-90) &#125;&#125;</div><div class='line highlight'>  material=&#123;new MeshStandardMaterial(&#123; color: 'white' &#125;)&#125;</div><div class='line highlight'>  geometry=&#123;new CircleGeometry(3, 72)&#125; /&gt;</div></code></div></pre><!-- HTML_TAG_END --><p>We load the dice by declaring that we are to use a <code>.obj</code> loader, and asynchronously load it when the svelte component is ready.</p><p>And we update the <code>&lt;T.Mesh&gt;</code> component to one of the clearer threlte wrapped <code>&lt;Mesh&gt;</code> components. With this component it is pretty easy to just jack in our loaded obj mesh.</p><p>Before moving on to physics and dice rolling, lets throw a texture on the dice so that we can see the value of each face. I hastily made one that you can find <a href="/assets/d20.jpg">here</a>. It does not look good, but it gets the job done.</p><div class="h-96"><canvas class="svelte-o3oskp"></canvas></div><!-- HTML_TAG_START --><pre class="shiki material-default with-title" html="true" title="diceRoller.svelte"><div class='code-title'>diceRoller.svelte</div><div class="language-id">html</div><div class='code-container'><code><div class='line'>&lt;script&gt;</div><div class='line'>  let d20Texture</div><div class='line'>  const loader = useLoader(OBJLoader, () =&gt; new OBJLoader())</div><div class='line'>  const textureLoader = useLoader(TextureLoader, () =&gt; new TextureLoader())</div><div class='line'>  onMount(() =&gt; &#123;</div><div class='line'>    textureLoader.load('/assets/d20.jpg', texture =&gt; &#123;</div><div class='line'>      texture.magFilter = NearestFilter</div><div class='line'>      texture.minFilter = LinearMipMapNearestFilter</div><div class='line'>      d20Texture = texture</div><div class='line'>    &#125;)</div><div class='line'>  &#125;)</div><div class='line'>&lt;/script&gt;</div><div class='line'>  &lt;Mesh</div><div class='line'>    position=&#123;&#123; x: 0, y: 1.5, z: 0 &#125;&#125;</div><div class='line'>    castShadow</div><div class='line'>    geometry=&#123;d20Mesh&#125;</div><div class='line'>    material=&#123;new MeshStandardMaterial(&#123; map: d20Texture, side: DoubleSide &#125;)&#125; /&gt;</div></code></div></pre><!-- HTML_TAG_END --><p>Somewhat abreviated, but pretty straightforwards.In the same way as with the dice we declare that we want to use a <code>TextureLoader</code>, and use it to load our texture jpg. The texture can then be added easily to our dice mesh material.Note that the <code>magFilter</code> and <code>minFilter</code> settings aren’t needed, but it indicates to threejs how it should scale/shrink textures.</p><h1 id="throwing-dice"><a href="#throwing-dice">Throwing dice</a></h1><p>Alright, lets get to the interesting stuff.I wish I could give you more of these “live views” but unfortunately rapier does not play nice when there is more that one physics world loaded.</p><p>But lets get into it anyways.</p><p>So to begin with we need to wrap our scene in a <code>&lt;World&gt;</code> tag.And then we need some static colliders</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" html="true" title="diceRoller.svelte"><div class='code-title'>diceRoller.svelte</div><div class="language-id">html</div><div class='code-container'><code><div class='line'>  &lt;!--floor--&gt;</div><div class='line'>  &lt;Collider shape="cuboid" args=&#123;[100, 0.1, 100]&#125; /&gt;</div><div class='line'></div><div class='line'>  &lt;!--top and bottom wall--&gt;</div><div class='line'>  &lt;Collider shape="cuboid" args=&#123;[1000, 100, 1]&#125; position=&#123;&#123; x: 0, y: 0, z: 10 &#125;&#125; /&gt;</div><div class='line'>  &lt;Collider shape="cuboid" args=&#123;[1000, 100, 1]&#125; position=&#123;&#123; x: 0, y: 0, z: -7 &#125;&#125; /&gt;</div><div class='line'>  &lt;!--left and right wall--&gt;</div><div class='line'>  &lt;Collider shape="cuboid" args=&#123;[1, 100, 1000]&#125; position=&#123;&#123; x: 10, y: 0, z: 0 &#125;&#125; /&gt;</div><div class='line'>  &lt;Collider shape="cuboid" args=&#123;[1, 100, 1000]&#125; position=&#123;&#123; x: -10, y: 0, z: 0 &#125;&#125; /&gt;</div></code></div></pre><!-- HTML_TAG_END --><p>These colliders are there so that our dice don’t just fall into the void, and they are somewhat contained within our viewport.Without knowing I feel pretty confident in saying that this for sure is not the smartest approach, and you would want something that is easier to adapt to whatever the camera i seeing/the canvas ration. <em>But</em> this works, and it is good enough for experimenting with.</p><p>Now that we have the static colliders defined, we need to add a rigid body to our dice.And to clarify a collider is a static object that is not moving and that is not rendered. A rigidbody is affected by physics, can be rendered, and collides with colliders and other rigid bodies.</p><!-- HTML_TAG_START --><pre class="shiki material-default" html="true"><div class="language-id">html</div><div class='code-container'><code><div class='line'>      &lt;RigidBody</div><div class='line'>        bind:rigidBody=&#123;d&#125;</div><div class='line'>        position=&#123;&#123; x: Math.random(), y: 4, z: 8 &#125;&#125;</div><div class='line'>        linearVelocity=&#123;&#123; x: Math.random() * 8 - 4, y: Math.random(), z: Math.random() * -20 &#125;&#125;</div><div class='line'>        angularVelocity=&#123;&#123;</div><div class='line'>          x: Math.random() * 3,</div><div class='line'>          y: Math.random() * 3,</div><div class='line'>          z: Math.random() * 3</div><div class='line'>        &#125;&#125;&gt;</div><div class='line'>        &lt;AutoColliders shape="convexHull" mass=&#123;0&#125;&gt;</div><div class='line'>          &lt;Mesh /&gt;</div><div class='line'>        &lt;/AutoColliders&gt;</div><div class='line'>      &lt;/RigidBody&gt;</div></code></div></pre><!-- HTML_TAG_END --><p>So by wrapping our dice with <code>&lt;RigidBody&gt;</code> and <code>&lt;AutoColliders&gt;</code> it becomes part of the physics simulation.</p><p>Note that we are setting a both a linear and an angular velocity on the rigid body, meaning that we will “throw” it with some spin applied.</p><p>Additionally and this is <strong>very important</strong>, you need to remove the position from the mesh and add it soley to the rigid body. We will go into more details about this later.</p><p>But with all of this we should have something that looks like this</p><div class="h-96"><canvas class="svelte-o3oskp"></canvas></div><p>It is not perfect, but it works and I am pretty happy with it, even though I wouldn’t push it to anything near production ready code.</p><h1 id="reading-the-dice"><a href="#reading-the-dice">Reading the dice</a></h1><p>This part took me a bit over a day to figure out, but we need to read the face of the dice we just threw.In theory there is a pretty simple way of doing it. Just do a <a href="https://threejs.org/docs/#api/en/core/Raycaster" rel="nofollow noopener noreferrer external" target="_blank">raycast</a> from straight above each dice down into the ground.The raycast should give two faces, the top and bottom one. The bottom one can easily be discarded because it is further from the raycast origin. With the top face you can then cross reference the mesh face index with the face value… sounds simple, so why did it take me so long to do, and why did I say that it is simple in theory?</p><p>Well, as I mentioned when adding the <code>&lt;RigidBody&gt;</code> tag it is important to move the position away from the <code>&lt;Mesh&gt;</code> tag. I had overlooked that part, and thus I was not sending a raycast from the center of the dice, and in some scenarios I completely missed the dice altogether.</p><p>The code for it is pretty simple though</p><!-- HTML_TAG_START --><pre class="shiki material-default" js="true"><div class="language-id">js</div><div class='code-container'><code><div class='line'>// faceindex to dice face</div><div class='line'>let faceToDice = &#123;</div><div class='line'>  3: 1,</div><div class='line'>  16: 2,</div><div class='line'>  1: 3,</div><div class='line'>  18: 4,</div><div class='line'>  12: 5,</div><div class='line'>  14: 6,</div><div class='line'>  2: 7,</div><div class='line'>  10: 8,</div><div class='line'>  9: 9,</div><div class='line'>  6: 10,</div><div class='line'>  13: 11,</div><div class='line'>  11: 12,</div><div class='line'>  8: 13,</div><div class='line'>  19: 14,</div><div class='line'>  7: 15,</div><div class='line'>  5: 16,</div><div class='line'>  0: 17,</div><div class='line'>  17: 18,</div><div class='line'>  4: 19,</div><div class='line'>  15: 20</div><div class='line'>&#125;</div><div class='line'>dice.forEach((d, i) =&gt; &#123;</div><div class='line'>  //Get the position of the dice</div><div class='line'>  let trans = d.translation()</div><div class='line'>  //send the raycast from above the dice in a downwards direction</div><div class='line'>  const raycaster = new Raycaster(new Vector3(trans.x, trans.y + 5, trans.z), new Vector3(0, -1, 0), 0.1, 15)</div><div class='line'>  let intersects = raycaster.intersectObjects(three.scene.children, true)</div><div class='line'>  //The raycast could intersect with more than one dice, so filter out our own</div><div class='line'>  intersects = intersects.filter(intersect =&gt; intersect.object.id === diceMesh[i].id)</div><div class='line'>  let inter = intersects[0] // raycast will likely exit the other side and give 2 faces. ordered by distance first should be pointing up</div><div class='line'>  console.log(&#96;rolled a $&#123;faceToDice[inter.faceIndex]&#125;&#96;)</div><div class='line'>&#125;)</div></code></div></pre><!-- HTML_TAG_END --><h1 id="ending-words"><a href="#ending-words">Ending words</a></h1><p>That is basically it. Not an extremely complex task, but it took more time than I imagined.Is this the right way of doing it? no, 100% not. For instance I wouldn’t trust that these dice are perfectly random, even though they should be in theory.</p><p>To do this “right” I think it is a better approach to run through the simulation and check what <code>faceIndex</code> is pointing up, and then with a normal <code>Math.random()</code> paint on the value on that face, instead of leaving the randomness up to the physics engine.</p><h2 id="final-code"><a href="#final-code">Final code</a></h2><p>In case you want to reference my code, here it is</p><!-- HTML_TAG_START --><pre class="shiki material-default with-title" html="true" title="diceRoller.svelte"><div class='code-title'>diceRoller.svelte</div><div class="language-id">html</div><div class='code-container'><code><div class='line'>&lt;script&gt;</div><div class='line'>  import &#123; useLoader, DirectionalLight, AmbientLight, PerspectiveCamera, Mesh, useThrelte &#125; from '@threlte/core'</div><div class='line'>  import &#123; OBJLoader &#125; from 'three/examples/jsm/loaders/OBJLoader'</div><div class='line'>  import &#123; OrbitControls, T &#125; from '@threlte/core'</div><div class='line'>  import &#123; World, RigidBody, AutoColliders, Collider &#125; from '@threlte/rapier'</div><div class='line'>  import &#123;</div><div class='line'>    MeshStandardMaterial,</div><div class='line'>    ShadowMaterial,</div><div class='line'>    CircleGeometry,</div><div class='line'>    TextureLoader,</div><div class='line'>    NearestFilter,</div><div class='line'>    LinearMipMapNearestFilter,</div><div class='line'>    Raycaster,</div><div class='line'>    Vector3,</div><div class='line'>    DoubleSide</div><div class='line'>  &#125; from 'three'</div><div class='line'>  import &#123; degToRad &#125; from 'three/src/math/MathUtils'</div><div class='line'>  import &#123; spring &#125; from 'svelte/motion'</div><div class='line'>  import &#123; onMount &#125; from 'svelte'</div><div class='line'></div><div class='line'>  // faceindex to dice face</div><div class='line'>  let faceToDice = &#123;</div><div class='line'>    3: 1,</div><div class='line'>    16: 2,</div><div class='line'>    1: 3,</div><div class='line'>    18: 4,</div><div class='line'>    12: 5,</div><div class='line'>    14: 6,</div><div class='line'>    2: 7,</div><div class='line'>    10: 8,</div><div class='line'>    9: 9,</div><div class='line'>    6: 10,</div><div class='line'>    13: 11,</div><div class='line'>    11: 12,</div><div class='line'>    8: 13,</div><div class='line'>    19: 14,</div><div class='line'>    7: 15,</div><div class='line'>    5: 16,</div><div class='line'>    0: 17,</div><div class='line'>    17: 18,</div><div class='line'>    4: 19,</div><div class='line'>    15: 20</div><div class='line'>  &#125;</div><div class='line'></div><div class='line'>  const three = useThrelte()</div><div class='line'></div><div class='line'>  const scale = spring(1)</div><div class='line'></div><div class='line'>  const angularStrength = 10</div><div class='line'></div><div class='line'>  /**</div><div class='line'>   * @type &#123;import("three").BufferGeometry&#125;</div><div class='line'>   */</div><div class='line'>  let d20Mesh</div><div class='line'>  let d20Texture</div><div class='line'></div><div class='line'>  const loader = useLoader(OBJLoader, () =&gt; new OBJLoader())</div><div class='line'>  const textureLoader = useLoader(TextureLoader, () =&gt; new TextureLoader())</div><div class='line'>  onMount(() =&gt; &#123;</div><div class='line'>    textureLoader.load('/assets/d20.jpg', texture =&gt; &#123;</div><div class='line'>      texture.magFilter = NearestFilter</div><div class='line'>      texture.minFilter = LinearMipMapNearestFilter</div><div class='line'>      d20Texture = texture</div><div class='line'>    &#125;)</div><div class='line'>    loader.load('/assets/d20.obj', obj =&gt; &#123;</div><div class='line'>      d20Mesh = obj.children[0].geometry</div><div class='line'>      d20Mesh.computeVertexNormals()</div><div class='line'>      d20Mesh.normalizeNormals()</div><div class='line'>      console.log(d20Mesh)</div><div class='line'>    &#125;)</div><div class='line'>    setTimeout(rollDice, 300)</div><div class='line'>  &#125;)</div><div class='line'>  function random(min, max) &#123;</div><div class='line'>    // min and max included</div><div class='line'>    return Math.random() * (max - min + 1) + min</div><div class='line'>  &#125;</div><div class='line'>  /**</div><div class='line'>   * @type &#123;Array.&lt;import('@dimforge/rapier3d-compat').RigidBody&gt;&#125;</div><div class='line'>   */</div><div class='line'>  let dice = new Array(10).fill(undefined)</div><div class='line'>  /**</div><div class='line'>   * @type &#123;Array.&lt;import("three").BufferGeometry&gt;&#125;</div><div class='line'>   */</div><div class='line'>  let diceMesh = new Array(10).fill(undefined)</div><div class='line'></div><div class='line'>  /**</div><div class='line'>   * @type &#123;import("three").PerspectiveCamera&#125;</div><div class='line'>   */</div><div class='line'>  let camera</div><div class='line'>  async function rollDice() &#123;</div><div class='line'>    dice.forEach((d, i) =&gt; &#123;</div><div class='line'>      d.setTranslation(&#123; x: random(-1, 1), y: 4, z: 8 &#125;, true)</div><div class='line'>      d.setLinvel(&#123; x: random(-2, -2), y: 0, z: random(-18, -14) &#125;, true)</div><div class='line'>      d.setAngvel(</div><div class='line'>        &#123;</div><div class='line'>          x: random(-angularStrength, angularStrength),</div><div class='line'>          y: random(-angularStrength, angularStrength),</div><div class='line'>          z: random(-angularStrength, angularStrength)</div><div class='line'>        &#125;,</div><div class='line'>        true</div><div class='line'>      )</div><div class='line'>    &#125;)</div><div class='line'>    await waitUntilStopped()</div><div class='line'>    console.log('dice roll complete')</div><div class='line'>    dice.forEach((d, i) =&gt; &#123;</div><div class='line'>      let trans = d.translation()</div><div class='line'>      const raycaster = new Raycaster(new Vector3(trans.x, trans.y + 5, trans.z), new Vector3(0, -1, 0), 0.1, 15)</div><div class='line'>      let intersects = raycaster.intersectObjects(three.scene.children, true)</div><div class='line'></div><div class='line'>      intersects = intersects.filter(intersect =&gt; intersect.object.id === diceMesh[i].id)</div><div class='line'>      if (intersects.length == 2) &#123;</div><div class='line'>        let inter = intersects[0] // raycast will likely exit the other side and give 2 faces. ordered by distance first should be pointing up</div><div class='line'>        console.log(&#96;rolled a $&#123;faceToDice[inter.faceIndex]&#125;&#96;)</div><div class='line'>      &#125; else &#123;</div><div class='line'>        console.log('broken dice')</div><div class='line'>        if (intersects.length == 1)</div><div class='line'>          console.log(&#96;only found one face at $&#123;intersects[0].distance&#125;. Should have gotten an exit hole&#96;)</div><div class='line'>        else &#123;</div><div class='line'>          console.log('did not find anything')</div><div class='line'>        &#125;</div><div class='line'>      &#125;</div><div class='line'>    &#125;)</div><div class='line'>  &#125;</div><div class='line'></div><div class='line'>  let last</div><div class='line'>  async function waitUntilStopped() &#123;</div><div class='line'>    const precision = 4</div><div class='line'>    const diceIndex = d =&gt; (&#123; trans: d.translation(), rot: d.rotation() &#125;)</div><div class='line'>    if (!last) &#123;</div><div class='line'>      last = dice.map(diceIndex)</div><div class='line'>    &#125; else if (</div><div class='line'>      dice.map(diceIndex).every((dI, i) =&gt; &#123;</div><div class='line'>        return (</div><div class='line'>          dI.trans.x.toFixed(precision) === last[i].trans.x.toFixed(precision) &&</div><div class='line'>          dI.trans.y.toFixed(precision) === last[i].trans.y.toFixed(precision) &&</div><div class='line'>          dI.trans.z.toFixed(precision) === last[i].trans.z.toFixed(precision) &&</div><div class='line'>          dI.rot.x.toFixed(precision) === last[i].rot.x.toFixed(precision) &&</div><div class='line'>          dI.rot.y.toFixed(precision) === last[i].rot.y.toFixed(precision) &&</div><div class='line'>          dI.rot.z.toFixed(precision) === last[i].rot.z.toFixed(precision)</div><div class='line'>        )</div><div class='line'>      &#125;)</div><div class='line'>    ) &#123;</div><div class='line'>      return</div><div class='line'>    &#125;</div><div class='line'></div><div class='line'>    last = dice.map(diceIndex)</div><div class='line'>    await pause()</div><div class='line'>    return waitUntilStopped()</div><div class='line'>  &#125;</div><div class='line'></div><div class='line'>  function pause() &#123;</div><div class='line'>    return new Promise(function (resolve, reject) &#123;</div><div class='line'>      setTimeout(resolve, 150)</div><div class='line'>    &#125;)</div><div class='line'>  &#125;</div><div class='line'>&lt;/script&gt;</div><div class='line'></div><div class='line'>&lt;PerspectiveCamera bind:camera position=&#123;&#123; x: 0, y: 40, z: 0 &#125;&#125; fov=&#123;24&#125;&gt;</div><div class='line'>  &lt;OrbitControls maxPolarAngle=&#123;degToRad(80)&#125; enableZoom=&#123;true&#125; target=&#123;&#123; y: 0.5 &#125;&#125; /&gt;</div><div class='line'>&lt;/PerspectiveCamera&gt;</div><div class='line'>&lt;DirectionalLight</div><div class='line'>  shadow=&#123;&#123;</div><div class='line'>    mapSize: [2048, 2048],</div><div class='line'>    camera: &#123;</div><div class='line'>      left: -20,</div><div class='line'>      right: 20,</div><div class='line'>      top: 20,</div><div class='line'>      bottom: -20,</div><div class='line'>      near: 0.0001,</div><div class='line'>      far: 100</div><div class='line'>    &#125;,</div><div class='line'>    radius: 8</div><div class='line'>  &#125;&#125;</div><div class='line'>  intensity=&#123;0.1&#125;</div><div class='line'>  target=&#123;&#123; x: 1 &#125;&#125;</div><div class='line'>  position=&#123;&#123; x: 0, y: 10, z: 10 &#125;&#125; /&gt;</div><div class='line'>&lt;DirectionalLight position=&#123;&#123; x: -3, y: 10, z: -10 &#125;&#125; intensity=&#123;0.3&#125; /&gt;</div><div class='line'>&lt;AmbientLight intensity=&#123;0.2&#125; /&gt;</div><div class='line'>&lt;World&gt;</div><div class='line'>  &#123;#if d20Mesh && d20Texture&#125;</div><div class='line'>    &#123;#each dice as d, i&#125;</div><div class='line'>      &lt;RigidBody</div><div class='line'>        bind:rigidBody=&#123;d&#125;</div><div class='line'>        position=&#123;&#123; x: Math.random(), y: 4, z: 8 &#125;&#125;</div><div class='line'>        linearVelocity=&#123;&#123; x: Math.random() * 8 - 4, y: Math.random(), z: Math.random() * -20 &#125;&#125;</div><div class='line'>        angularVelocity=&#123;&#123;</div><div class='line'>          x: Math.random() * angularStrength,</div><div class='line'>          y: Math.random() * angularStrength,</div><div class='line'>          z: Math.random() * angularStrength</div><div class='line'>        &#125;&#125;&gt;</div><div class='line'>        &lt;AutoColliders shape="convexHull" mass=&#123;0&#125;&gt;</div><div class='line'>          &lt;!-- Cube --&gt;</div><div class='line'>          &lt;T.Group scale=&#123;$scale&#125;&gt;</div><div class='line'>            &lt;Mesh</div><div class='line'>              bind:mesh=&#123;diceMesh[i]&#125;</div><div class='line'>              position=&#123;&#123; x: 0, y: 0, z: 0 &#125;&#125;</div><div class='line'>              castShadow</div><div class='line'>              geometry=&#123;d20Mesh&#125;</div><div class='line'>              material=&#123;new MeshStandardMaterial(&#123; map: d20Texture, side: DoubleSide &#125;)&#125; /&gt;</div><div class='line'>          &lt;/T.Group&gt;</div><div class='line'>        &lt;/AutoColliders&gt;</div><div class='line'>      &lt;/RigidBody&gt;</div><div class='line'>    &#123;/each&#125;</div><div class='line'>  &#123;/if&#125;</div><div class='line'></div><div class='line'>  &lt;Collider shape="cuboid" args=&#123;[100, 0.1, 100]&#125; /&gt;</div><div class='line'></div><div class='line'>  &lt;Collider shape="cuboid" args=&#123;[1000, 100, 1]&#125; position=&#123;&#123; x: 0, y: 0, z: 10 &#125;&#125; /&gt;</div><div class='line'>  &lt;Collider shape="cuboid" args=&#123;[1000, 100, 1]&#125; position=&#123;&#123; x: 0, y: 0, z: -7 &#125;&#125; /&gt;</div><div class='line'></div><div class='line'>  &lt;Collider shape="cuboid" args=&#123;[1, 100, 1000]&#125; position=&#123;&#123; x: 10, y: 0, z: 0 &#125;&#125; /&gt;</div><div class='line'>  &lt;Collider shape="cuboid" args=&#123;[1, 100, 1000]&#125; position=&#123;&#123; x: -10, y: 0, z: 0 &#125;&#125; /&gt;</div><div class='line'></div><div class='line'>  &lt;Mesh</div><div class='line'>    receiveShadow</div><div class='line'>    interactive</div><div class='line'>    on:click=&#123;rollDice&#125;</div><div class='line'>    rotation=&#123;&#123; x: degToRad(-90) &#125;&#125;</div><div class='line'>    material=&#123;new ShadowMaterial(&#123; color: '#050505' &#125;)&#125;</div><div class='line'>    geometry=&#123;new CircleGeometry(300, 72)&#125; /&gt;</div><div class='line'>&lt;/World&gt;</div></code></div></pre><!-- HTML_TAG_END -->]]>
    </content>
    <category term="DnD" scheme="https://blog.wunderdev.com/?tags=DnD" />
    <category term="javascript" scheme="https://blog.wunderdev.com/?tags=javascript" />
    <category term="three js" scheme="https://blog.wunderdev.com/?tags=three%20js" />
    <category term="threlte" scheme="https://blog.wunderdev.com/?tags=threlte" />
    <category term="rapier" scheme="https://blog.wunderdev.com/?tags=rapier" />
  </entry>
  <entry>
    <title type="html"><![CDATA[Dice parser]]></title>
    <link href="https://blog.wunderdev.com/blog/DnD/2" />
    <id>https://blog.wunderdev.com/blog/DnD/2</id>
    <published>2023-01-28T00:00:00.000Z</published>
    <updated>2023-01-28T00:00:00.000Z</updated>
    <summary type="html"><![CDATA[Writing a parser for a simplistic dice expression using nearley]]></summary>
    <content type="html">
      <![CDATA[<p>I got the urge to write a parser for table top gaming.For example <code>4d6+1</code> meaning roll 4 6 sided dices, sum it all up and add 1 to the result.A pretty simple task, that could probably be done by just looking at the string and doing some splits or substrings. But I want to do it with <a href="https://www.npmjs.com/package/nearley" rel="nofollow noopener noreferrer external" target="_blank">nearley</a>, because that sound like fun to me.</p><h1 id="basics"><a href="#basics">Basics</a></h1><p>Lets begin with the most basic example of <code>4d6</code>. It has 3 components, a number followed by a letter <code>d</code> followed by another number. So lets create a parser for it with nearley</p><!-- HTML_TAG_START --><pre class="shiki material-default"><div class='code-container'><code><div class='line'># this is our starting rule</div><div class='line'>Main -&gt; int "d" int</div><div class='line'></div><div class='line'># We are only interested in integers. floats does not make sense here</div><div class='line'>int -&gt; [0-9]:+        &#123;% (d) =&gt; parseInt(d.flat().join("")) %&#125;</div></code></div></pre><!-- HTML_TAG_END --><p>Note that the <code>int</code> rule produces an array of strings, so with the postprocessing function we can make sure that it outputs an integer.</p><p>So, parsing <code>4d6</code> will output <code>[4, &#39;d&#39;, 6]</code>.</p><p>Next up, lets add a parser for the optional <code>+1</code> at the end</p><!-- HTML_TAG_START --><pre class="shiki material-default"><div class='code-container'><code><div class='line'>Main -&gt; int "d" int</div><div class='line'>Main -&gt; int "d" int "+" int</div></code></div></pre><!-- HTML_TAG_END --><p>And we are pretty much done. But I think we can do better, by adding a few features.To begin with it would be good if we could use more than one type of dice. For instance 2 6 sided dices and 1 10 sided dice <code>2d6 1d10</code>.</p><h1 id="multiple-dice-and-recursion"><a href="#multiple-dice-and-recursion">Multiple dice and recursion</a></h1><p>To do this we can begin by refactoring the main rule into a dice rule</p><!-- HTML_TAG_START --><pre class="shiki material-default"><div class='code-container'><code><div class='line'>Main -&gt; Dice</div><div class='line'></div><div class='line'>Dice -&gt; int "d" int         &#123;% (d) =&gt; (&#123;count: d[0], sides: d[2], bonus: 0&#125;) %&#125;</div><div class='line'>      | int "d" int "+" int &#123;% (d) =&gt; (&#123;count: d[0], sides: d[2], bonus: d[4]&#125;) %&#125;</div></code></div></pre><!-- HTML_TAG_END --><p>Note that we are also formatting the output into an object to make it less error prone.Now to add multiple dices it is as easy as adding right hand recursion on the <code>Main</code> rule.</p><!-- HTML_TAG_START --><pre class="shiki material-default"><div class='code-container'><code><div class='line'>Main -&gt; Dice</div><div class='line'>      | Dice " " Main &#123;% (d) =&gt; [d[0], d[2]].flat()%&#125;</div></code></div></pre><!-- HTML_TAG_END --><p>The post processing step on the second rule makes it so that we get an easy to parse output with an array of dice objects, instead of a deeply nested array.</p><h1 id="advantagedisadvantage"><a href="#advantagedisadvantage">Advantage/Disadvantage</a></h1><p>Last thing I want to add to the language is something specific to dungeons and dragons. In that game it is common to throw twenty sided dices with either advantage or disadvantage. This means you throw the dice twice and keep the largest or smallest result respectively.</p><p>I think I want to represent this as <code>1d20a</code> or <code>1d20d</code>. This got a bit messy, but here are the rules I made for it</p><!-- HTML_TAG_START --><pre class="shiki material-default"><div class='code-container'><code><div class='line'>Main -&gt; BonusDice</div><div class='line'>      | BonusDice " " Main &#123;% (d) =&gt; [d[0], d[2]].flat()%&#125;</div><div class='line'></div><div class='line'></div><div class='line'>BonusDice -&gt; EnhancedDice　        &#123;% (d) =&gt; (&#123;...d[0], bonus: 0&#125;) %&#125;</div><div class='line'>       | EnhancedDice "+" int  &#123;% (d) =&gt; (&#123;...d[0], bonus: d[2]&#125;) %&#125;</div><div class='line'></div><div class='line'>EnhancedDice -&gt; Dice      &#123;% (d) =&gt; (&#123;...d[0], advantage: false, disadvantage: false&#125;) %&#125;</div><div class='line'>              | Dice "a"  &#123;% (d) =&gt; (&#123;...d[0], advantage: true, disadvantage: false&#125;) %&#125;</div><div class='line'>              | Dice "b"  &#123;% (d) =&gt; (&#123;...d[0], advantage: false, disadvantage: true&#125;) %&#125;</div><div class='line'></div><div class='line'>Dice -&gt; int "d" int         &#123;% (d) =&gt; (&#123;count: d[0], sides: d[2]&#125;) %&#125;</div></code></div></pre><!-- HTML_TAG_END --><h1 id="polish"><a href="#polish">Polish</a></h1><p>The last convenience I want in the language is the option to not specify how many dices to throw, and default to 1. So <code>1d20</code> should be the same as <code>d20</code>. Pretty simple to just add to <code>Dice</code></p><!-- HTML_TAG_START --><pre class="shiki material-default"><div class='code-container'><code><div class='line'>Dice -&gt; int "d" int         &#123;% (d) =&gt; (&#123;count: d[0], sides: d[2]&#125;) %&#125;</div><div class='line'>      | "d" int             &#123;% (d) =&gt; (&#123;count: 1, sides: d[1]&#125;) %&#125;</div></code></div></pre><!-- HTML_TAG_END --><p>That is basically it!The output can easily be parsed and you can create a dice roller.Or, you can do like I did and add the dice rolling straight into the parser.</p><!-- HTML_TAG_START --><pre class="shiki material-default"><div class='code-container'><code><div class='line'># this is our starting rule</div><div class='line'>Main -&gt; MultiDice &#123;% (d) =&gt; d[0].map(v =&gt; (&#123;...v, sum: v.rolls.reduce((acc, val) =&gt; acc + val) + v.bonus&#125;)) %&#125;</div><div class='line'></div><div class='line'>MultiDice -&gt; BonusDice</div><div class='line'>           | BonusDice " " MultiDice &#123;% (d) =&gt; [d[0], d[2]].flat()%&#125;</div><div class='line'></div><div class='line'>BonusDice -&gt; RolledDice　        &#123;% (d) =&gt; (&#123;...d[0], bonus: 0&#125;) %&#125;</div><div class='line'>         | RolledDice "+" int  &#123;% (d) =&gt; (&#123;...d[0], bonus: d[2]&#125;) %&#125;</div><div class='line'></div><div class='line'>#Do the roll directly in the parsing</div><div class='line'>RolledDice -&gt; EnhancedDice &#123;% (d) =&gt; (&#123;...d[0], rolls:</div><div class='line'>   new Array(d[0].count)</div><div class='line'>   .fill(undefined)</div><div class='line'>   .map(() =&gt; &#123;</div><div class='line'>   //Create two rolls for each dice</div><div class='line'>   const rolls = new Array(2).fill(undefined).map(() =&gt; Math.floor(Math.random() * d[0].sides + 1))</div><div class='line'>   if(d[0].advantage)</div><div class='line'>   return Math.max(rolls[0], rolls[1])</div><div class='line'>   if(d[0].disadvantage)</div><div class='line'>   return Math.min(rolls[0], rolls[1])</div><div class='line'>   else return rolls[0]</div><div class='line'>   &#125;)&#125;) %&#125;</div><div class='line'></div><div class='line'>EnhancedDice -&gt; Dice      &#123;% (d) =&gt; (&#123;...d[0], advantage: false, disadvantage: false&#125;) %&#125;</div><div class='line'>              | Dice "a"  &#123;% (d) =&gt; (&#123;...d[0], advantage: true, disadvantage: false&#125;) %&#125;</div><div class='line'>  | Dice "b"  &#123;% (d) =&gt; (&#123;...d[0], advantage: false, disadvantage: true&#125;) %&#125;</div><div class='line'></div><div class='line'>Dice -&gt; int "d" int         &#123;% (d) =&gt; (&#123;count: d[0], sides: d[2]&#125;) %&#125;</div><div class='line'>      | "d" int             &#123;% (d) =&gt; (&#123;count: 1, sides: d[1]&#125;) %&#125;</div><div class='line'></div><div class='line'>int -&gt; [0-9]:+        &#123;% (d) =&gt; parseInt(d.flat().join("")) %&#125;</div></code></div></pre><!-- HTML_TAG_END --><p>It got a bit messy, but pretty neat to get an output with a list of all the dice values and the result of adding them all together</p><p>Here is an example of the output if run with the input <code>1d20a+2 d4+1</code></p><!-- HTML_TAG_START --><pre class="shiki material-default" json="true"><div class="language-id">json</div><div class='code-container'><code><div class='line'>[</div><div class='line'>  &#123;</div><div class='line'>    count: 1,</div><div class='line'>    sides: 20,</div><div class='line'>    advantage: true,</div><div class='line'>    disadvantage: false,</div><div class='line'>    rolls: [14],</div><div class='line'>    bonus: 2,</div><div class='line'>    sum: 16</div><div class='line'>  &#125;,</div><div class='line'>  &#123;</div><div class='line'>    count: 1,</div><div class='line'>    sides: 4,</div><div class='line'>    advantage: false,</div><div class='line'>    disadvantage: false,</div><div class='line'>    rolls: [4],</div><div class='line'>    bonus: 1,</div><div class='line'>    sum: 5</div><div class='line'>  &#125;</div><div class='line'>]</div></code></div></pre><!-- HTML_TAG_END --><h1 id="final-words"><a href="#final-words">Final words</a></h1><p>Nearley might not have been the best fit for this, but it was in all honesty really fun to use. And the result speaks for itself, it works and is pretty neat.The best part is that it is quite easy to build upon.</p><p>Now all that is missing is to starting using this system, and while implementing this I was thinking of creating a system like the one in <a href="https://www.dndbeyond.com/" rel="nofollow noopener noreferrer external" target="_blank">DnDBeyond</a> where you have 3D dice rolling on the screen.Would be fun to experiment with, and should be doable using <a href="https://threejs.org/" rel="nofollow noopener noreferrer external" target="_blank">three.js</a> without too much effort</p>]]>
    </content>
    <category term="DnD" scheme="https://blog.wunderdev.com/?tags=DnD" />
    <category term="javascript" scheme="https://blog.wunderdev.com/?tags=javascript" />
    <category term="typescript" scheme="https://blog.wunderdev.com/?tags=typescript" />
    <category term="nearley" scheme="https://blog.wunderdev.com/?tags=nearley" />
    <category term="compiler" scheme="https://blog.wunderdev.com/?tags=compiler" />
  </entry>
  <entry>
    <title type="html"><![CDATA[Dungeons and dragons query system]]></title>
    <link href="https://blog.wunderdev.com/blog/DnD/1" />
    <id>https://blog.wunderdev.com/blog/DnD/1</id>
    <published>2023-01-26T00:00:00.000Z</published>
    <updated>2023-01-26T00:00:00.000Z</updated>
    <summary type="html"><![CDATA[Creating a simple query engine for yaml files used in a DnD wiki]]></summary>
    <content type="html">
      <![CDATA[<h1 id="choosing-a-format"><a href="#choosing-a-format">Choosing a format</a></h1><p>For my upcoming Dungeons and Dragons I wanted to do all of my world building in a computer readable format.And I was pretty sure I wanted to write that format as well, so I picked <code>yaml</code>.One could ask why I didn’t pick ex markdown and it is because the structure wouldn’t feel right for me, and the metadata does not work the way I want it to.For example I think it would be a good idea to have one single yaml file for the whole of a political organization in my game.That one yaml file can then easily contain the history of the organization, all needed NPCs, and potential quest lines, something I don’t find viable in a markdown format.</p><h1 id="documentation-entry"><a href="#documentation-entry">Documentation entry</a></h1><p>Here is an example of a yaml file I created for my D&amp;D world (in a cyberpunk setting)</p><!-- HTML_TAG_START --><pre class="shiki material-default" yaml="true"><div class="language-id">yaml</div><div class='code-container'><code><div class='line'>name: Neon Chroma</div><div class='line'>description: The neon chroma is a really small bar with all seating out on the road. It is dimly lit with neon signs</div><div class='line'>npc:</div><div class='line'>  - name: Neo</div><div class='line'>    race: near human</div><div class='line'>    description: A charismatic android with a dry wit, Neo enjoys chatting with patrons about the latest tech advancements.</div><div class='line'>signatureDrink: |-</div><div class='line'>  Neo's Blissful Elixir: Create a unique blend of equal parts vodka, amaretto, and blueberry flavored syrup. Pour into an ice-filled shaker and shake vigorously. Serve in a martini glass with a lime wedge garnish.</div></code></div></pre><!-- HTML_TAG_END --><h1 id="backend"><a href="#backend">Backend</a></h1><p>I think this is a pretty nifty way of documenting, but there is <em>one</em> issue.Across the many files created, how do I find all the signature drinks that exists in my world?… ok, not a likely query, or thought to have, but you get the idea.</p><p>I suppose I could push it into a real indexing engine, such as Elasticsearch, but wow that would be such an overkill for a some homebrew game.Another less extreme option would be to use a normal NOSQL database such as mongo, which would be able to query this data for me.But, I would prefer to skip the complexity of having a separate server/database all together.</p><p>This led me to implement my own simple query system.</p><h1 id="querying"><a href="#querying">Querying</a></h1><!-- HTML_TAG_START --><pre class="shiki material-default" js="true"><div class="language-id">js</div><div class='code-container'><code><div class='line'>query(&#96;.signatureDrink&#96;)</div></code></div></pre><!-- HTML_TAG_END --><p>That query should be able to fetch all drinks for me.So how to implement it?</p><p>I did a naive approach of reading all yaml files into memory, which honestly works fine, my campaign really isn’t that big, and is unlikely to ever get so big that it becomes an issue.</p><p>I then wrote a small script</p><!-- HTML_TAG_START --><pre class="shiki material-default" typescript="true"><div class="language-id">typescript</div><div class='code-container'><code><div class='line'>function nextQry(query) &#123;</div><div class='line'>  if (query.substring(1).split("").includes(".")) &#123;</div><div class='line'>    return query.substring(1).split(".").slice(1).join(".");</div><div class='line'>  &#125;</div><div class='line'>&#125;</div><div class='line'>function arrayQuery(array, query) &#123;</div><div class='line'>  if (query.startsWith("[")) &#123;</div><div class='line'>    console.log("array search");</div><div class='line'>    const item = eval("array" + query.substring(0, query.indexOf("]") + 1));</div><div class='line'>    if (item && typeof item === "object") return anyQuery(item, nextQry(query));</div><div class='line'>    if (item) return item;</div><div class='line'>    return;</div><div class='line'>  &#125;</div><div class='line'>  if (!query.startsWith(".")) &#123;</div><div class='line'>    query = "." + query;</div><div class='line'>  &#125;</div><div class='line'>  var newArray = [];</div><div class='line'>  for (var i = 0; i &lt; array.length; i++) &#123;</div><div class='line'>    var item = array[i];</div><div class='line'>    if (typeof item === "object") &#123;</div><div class='line'>      const res = anyQuery(item, query);</div><div class='line'>      if (res) &#123;</div><div class='line'>        newArray.push(res);</div><div class='line'>      &#125;</div><div class='line'>    &#125; else if (eval("item" + query)) &#123;</div><div class='line'>      newArray.push(item);</div><div class='line'>    &#125;</div><div class='line'>  &#125;</div><div class='line'>  return newArray;</div><div class='line'>&#125;</div><div class='line'></div><div class='line'>function objectQuery(obj, query) &#123;</div><div class='line'>  if (!query.startsWith(".")) &#123;</div><div class='line'>    query = "." + query;</div><div class='line'>  &#125;</div><div class='line'>  const next = nextQry(query);</div><div class='line'>  const current = eval("obj" + query.split(".").slice(0, 2).join("."));</div><div class='line'>  if (next && current) return anyQuery(current, next);</div><div class='line'></div><div class='line'>  if (current) &#123;</div><div class='line'>    if (typeof current === "number" || typeof current === "string")</div><div class='line'>      return current;</div><div class='line'>    return obj;</div><div class='line'>  &#125;</div><div class='line'>&#125;</div><div class='line'></div><div class='line'>function anyQuery(anyData, query) &#123;</div><div class='line'>  if (Array.isArray(anyData)) return arrayQuery(anyData, query);</div><div class='line'>  return objectQuery(anyData, query);</div><div class='line'>&#125;</div></code></div></pre><!-- HTML_TAG_END --><p>And with that I could send an array of all my files (parsed from yaml) into <code>anyQuery</code> and get a list of all drinks in all files.</p><p>Of course this assumes that I spell the property correctly every time, but I suppose I can’t really get around human errors</p>]]>
    </content>
    <category term="DnD" scheme="https://blog.wunderdev.com/?tags=DnD" />
    <category term="javascript" scheme="https://blog.wunderdev.com/?tags=javascript" />
    <category term="typescript" scheme="https://blog.wunderdev.com/?tags=typescript" />
    <category term="query" scheme="https://blog.wunderdev.com/?tags=query" />
    <category term="data" scheme="https://blog.wunderdev.com/?tags=data" />
    <category term="yaml" scheme="https://blog.wunderdev.com/?tags=yaml" />
  </entry>
  <entry>
    <title type="html"><![CDATA[Creating a compiler for language learning]]></title>
    <link href="https://blog.wunderdev.com/blog/linitic/1" />
    <id>https://blog.wunderdev.com/blog/linitic/1</id>
    <published>2023-01-26T00:00:00.000Z</published>
    <updated>2023-01-26T00:00:00.000Z</updated>
    <summary type="html"><![CDATA[Writing a DSL compiler for my japanese learning app]]></summary>
    <content type="html">
      <![CDATA[<p>I’ve been trying to learn japanese for quite some time now, and just like everyone else I’ve used the “normal” apps such as duolingo to get the job done.And just like every other developer out there using an app, I convinced myself that I can make an app that is better than the immensely popular one I am already using… I am of course wrong in this, but it has been a fun journey.</p><h1 id="prototypes"><a href="#prototypes">Prototypes</a></h1><p>I started making a bunch of prototypes with a lot of confidence, and little knowledge of the domain.I remember one of my early prototypes where I got annoyed that a character could mean more than one thing <em>and</em> be pronounced in more than one way… Absolute madness, and obviously <em>“they”</em> made a mistake when creating the japanese language.</p><p>Anyways the early websites I made are not really the topic for this post, no matter how polished or unpolished they were. Instead I want to focus on the latest webapp I made.</p><h1 id="wishlist"><a href="#wishlist">Wishlist</a></h1><p>I had the idea to create a learning app with vocabulary lessons, grammar lessons, and story driven lessons where you try and have a scripted somewhat realistic chat conversation.And to top it all up, creating the lessons (or content) to the app had to be easy and not hindered by annoying repetitive formats such as json or yaml. Seems like an easy task right?</p><h1 id="compiler"><a href="#compiler">Compiler</a></h1><p>So I did what ever other sane language learner with a bit of programming know how would do… I wrote my own compiler for a custom DSL.</p><p>Now, don’t get me wrong: making a compiler is a massive overkill and can be a daunting task, but I had scoped it to such a level where it really wasn’t that big of a task.But anyways, I started out the way I always do, which can only be described as the wrong way. I started writing code first without doing the slightest bit of research on how compilers generally works, because how hard could it be?</p><p>Well turns out it is at least a bit tricky and my first iteration ended up as a pile of spaghetti code that I barely worked and was so rigid that I could forget about adding any features that I wanted.</p><p>So I scrapped it, and started doing a little bit of research and stumbled upon <a href="https://www.npmjs.com/package/nearley" rel="nofollow noopener noreferrer external" target="_blank">nearley</a> which I believe would be absolutely great for most compiler projects. Unfortunately it wasn’t really possible for me to use it because I could for the life of me figure out how to make it tab/space aware, and I wanted my language to have semantically important spaces the same way yaml does.</p><p>Back to square one and I decided to make everything myself… but which some background knowledge.So I made it the way I should have the first time, with a tokenizer and parser which gave me an output in the form of a parse tree.</p><p>I could then take that output and compile it into some svelte code, which worked great… unfortunately I completely burned out on the project before it got even remotely useful.</p><p>Here is an example of the input to the compiler</p><!-- HTML_TAG_START --><pre class="shiki material-default"><div class='code-container'><code><div class='line'>vocabulary:</div><div class='line'>  もしもし</div><div class='line'>  hello</div><div class='line'>  先[せん]週[しゅう]</div><div class='line'>  last week</div><div class='line'>grammar:</div><div class='line'>  title: い adjective conjugation</div><div class='line'>  conjugating in affirmative inflections</div><div class='line'>  present | past</div><div class='line'>  [い]です | [-い-]かったです</div><div class='line'>  ex:</div><div class='line'>    present  | past</div><div class='line'>    たのしいです | たのしかったです</div><div class='line'>    おいしいです | &#123;text hiragana | validate おいしかったです&#125;</div><div class='line'>    おおきいです | &#123;text hiragana | validate おおきかったです&#125;</div><div class='line'>story:</div><div class='line'>  &gt; 先[せん]週[しゅう]仕[し]事[ごと]はどうでしたか</div><div class='line'>    how was last weeks work?</div><div class='line'>  &lt; 忙[いそが]しかったです</div><div class='line'>    &#123;text hiragana | validate いそがしかったです&#125;</div><div class='line'>                   | suffix いです | hint "You are not conjugating to past tense"&#125;</div><div class='line'>    &#123;help "base for of busy is 忙[いそが]しい. To make any い adjective into past affirmative tense you remove the trailing い and add かった"&#125;</div><div class='line'>    It was busy</div></code></div></pre><!-- HTML_TAG_END --><p>oh well, for the next project at least I know how to write a compiler.</p>]]>
    </content>
    <category term="language learning" scheme="https://blog.wunderdev.com/?tags=language%20learning" />
    <category term="japanese" scheme="https://blog.wunderdev.com/?tags=japanese" />
    <category term="compiler" scheme="https://blog.wunderdev.com/?tags=compiler" />
    <category term="linitic" scheme="https://blog.wunderdev.com/?tags=linitic" />
  </entry>
</feed>