<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>MapDirection on blog</title>
    <link>https://jpcharalambosh.co/tags/mapdirection/</link>
    <description>Recent content in MapDirection on blog</description>
    <image>
      <title>blog</title>
      <url>https://jpcharalambosh.co/papermod-cover.png</url>
      <link>https://jpcharalambosh.co/papermod-cover.png</link>
    </image>
    <generator>Hugo</generator>
    <language>en</language>
    <lastBuildDate>Thu, 09 Apr 2026 12:00:00 -0500</lastBuildDate>
    <atom:link href="https://jpcharalambosh.co/tags/mapdirection/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>3D Brush Painting in VR</title>
      <link>https://jpcharalambosh.co/posts/brush/</link>
      <pubDate>Thu, 09 Apr 2026 12:00:00 -0500</pubDate>
      <guid>https://jpcharalambosh.co/posts/brush/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A 3D brush painting sketch that uses depth control for VR-style experiences. &lt;a href=&#34;https://github.com/VisualComputing/p5.tree&#34;&gt;&lt;code&gt;mapLocation()&lt;/code&gt;&lt;/a&gt; converts screen-space mouse coordinates and a depth slider value directly into world-space positions, so each brush stroke is placed precisely in 3D. Press &lt;code&gt;r&lt;/code&gt; to toggle recording, &lt;code&gt;c&lt;/code&gt; to clear, &lt;code&gt;f&lt;/code&gt; to re-focus the camera on the world origin.&lt;/p&gt;
&lt;/blockquote&gt;
















&lt;iframe
  id=&#39;p5-0&#39;
  style=&#39;border:none; display:block; width:0; height:0;&#39;
  srcdoc=&#34;
    &lt;!DOCTYPE html&gt;
    &lt;html&gt;
      &lt;head&gt;
        &lt;style&gt;body{margin:0;padding:0;overflow:hidden}&lt;/style&gt;
        &lt;script src=&#39;https://cdn.jsdelivr.net/npm/p5@2.2.3/lib/p5.min.js&#39;&gt;&lt;/script&gt;
        
        &lt;script src=&#39;https://cdn.jsdelivr.net/npm/p5.tree/dist/p5.tree.min.js&#39;&gt;&lt;/script&gt;
        
        
        
        
        
        
        
        
        &lt;script&gt;
          
&amp;#39;use strict&amp;#39;

let brushColor, depthSlider, points = []
let recording = false

function setup() {
  createCanvas(600, 400, WEBGL)
  colorMode(RGB, 1)
  document.oncontextmenu = () =&amp;gt; false

  // initialise depth slider at the screen-space z of the world origin
  const o = mapLocation(p5.Tree.ORIGIN, { from: p5.Tree.WORLD, to: p5.Tree.SCREEN })
  depthSlider = createSlider(0, 1, o.z, 0.001)
  depthSlider.position(10, 10)
  depthSlider.style(&amp;#39;width&amp;#39;, &amp;#39;580px&amp;#39;)

  brushColor = createColorPicker(&amp;#39;#C7C08D&amp;#39;)
  brushColor.position(width - 70, 40)
}

function draw() {
  mouseY &amp;gt;= 30 &amp;amp;&amp;amp; orbitControl()
  recording &amp;amp;&amp;amp; addPoint()

  background(&amp;#39;#222226&amp;#39;)
  axes({ size: 50, bits: p5.Tree.X | p5.Tree.Y | p5.Tree.Z })

  for (const pt of points) {
    push()
    noStroke()
    fill(pt.color)
    translate(pt.pos)
    sphere(1)
    pop()
  }
}

function addPoint() {
  points.push({
    pos:   mapLocation([mouseX, mouseY, depthSlider.value()],
                       { from: p5.Tree.SCREEN, to: p5.Tree.WORLD }),
    color: brushColor.color()
  })
}

function focusOrigin() {
  const eye    = mapLocation()                              // camera world position
  const up     = mapDirection(p5.Tree.j)                   // camera up in world space
  const origin = [0, 0, 0]
  camera(eye.x, eye.y, eye.z, ...origin, up.x, up.y, up.z)
  const o = mapLocation(p5.Tree.ORIGIN, { from: p5.Tree.WORLD, to: p5.Tree.SCREEN })
  depthSlider.value(o.z)
}

function keyPressed() {
  key === &amp;#39;c&amp;#39; &amp;amp;&amp;amp; (points = [])
  key === &amp;#39;f&amp;#39; &amp;amp;&amp;amp; focusOrigin()
  key === &amp;#39;r&amp;#39; &amp;amp;&amp;amp; (recording = !recording)
}

        &lt;/script&gt;
        
        &lt;script&gt;
          (function() {
            var t = setInterval(function() {
              var c = document.querySelector(&#39;canvas&#39;)
              if (c &amp;&amp; c.offsetWidth &gt; 0) {
                clearInterval(t)
                window.parent.postMessage({ type: &#39;p5resize&#39;, id: &#39;p5-0&#39;, w: c.offsetWidth, h: c.offsetHeight }, &#39;*&#39;)
              }
            }, 100)
          })()
        &lt;/script&gt;
      &lt;/head&gt;
      &lt;body&gt;&lt;/body&gt;
    &lt;/html&gt;
  &#34;
&gt;&lt;/iframe&gt;
&lt;script&gt;
window.addEventListener(&#39;message&#39;, function(e) {
  if (e.data &amp;&amp; e.data.type === &#39;p5resize&#39; &amp;&amp; e.data.id === &#39;p5-0&#39;) {
    var f = document.getElementById(&#39;p5-0&#39;)
    if (f) { f.style.width = e.data.w + &#39;px&#39;; f.style.height = e.data.h + &#39;px&#39; }
  }
})
&lt;/script&gt;



&lt;hr&gt;
&lt;h2 id=&#34;screen-to-world-mapping-with-maplocation&#34;&gt;Screen-to-world mapping with &lt;code&gt;mapLocation&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The core of the brush is a single &lt;code&gt;mapLocation&lt;/code&gt; call that lifts the 2D mouse position — together with a depth value from the slider — into 3D world space each frame:&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
