<?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>Interpolation on blog</title>
    <link>https://jpcharalambosh.co/tags/interpolation/</link>
    <description>Recent content in Interpolation 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>Sun, 19 Apr 2026 12:00:00 -0500</lastBuildDate>
    <atom:link href="https://jpcharalambosh.co/tags/interpolation/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Camera interpolation</title>
      <link>https://jpcharalambosh.co/posts/camera_interpolation/</link>
      <pubDate>Sun, 19 Apr 2026 12:00:00 -0500</pubDate>
      <guid>https://jpcharalambosh.co/posts/camera_interpolation/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Lookat-camera keyframe animation with &lt;a href=&#34;https://github.com/VisualComputing/p5.tree&#34;&gt;&lt;code&gt;createCameraTrack&lt;/code&gt;&lt;/a&gt;. A bound &lt;code&gt;animCam&lt;/code&gt; plays a four-keyframe track; a separate &lt;code&gt;viewCam&lt;/code&gt; orbits around the abstraction — eye polyline, gaze rays, per-keyframe mini-camera markers. A live frustum follows playback, and an FBO inset shows what &lt;code&gt;animCam&lt;/code&gt; actually sees. Toggle &lt;code&gt;TEX&lt;/code&gt; and that inset slides onto the live frustum&amp;rsquo;s near plane — the frustum becomes a window. Two &lt;a href=&#34;https://github.com/VisualComputing/p5.tree&#34;&gt;&lt;code&gt;createPanel&lt;/code&gt;&lt;/a&gt; instances drive the whole thing: one binds checkboxes to UI state, one binds transport controls to the track.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Pose interpolation</title>
      <link>https://jpcharalambosh.co/posts/pose_interpolation/</link>
      <pubDate>Sun, 19 Apr 2026 10:00:00 -0500</pubDate>
      <guid>https://jpcharalambosh.co/posts/pose_interpolation/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;TRS keyframe animation with &lt;a href=&#34;https://github.com/VisualComputing/p5.tree&#34;&gt;&lt;code&gt;createPoseTrack&lt;/code&gt;&lt;/a&gt;. Four &lt;code&gt;{ pos, rot }&lt;/code&gt; keyframes, cubic Hermite interpolation of position with auto-Catmull-Rom tangents, and slerp on rotations. A transport panel scrubs the track; a second panel toggles the &lt;a href=&#34;https://github.com/VisualComputing/p5.tree&#34;&gt;&lt;code&gt;trackPath&lt;/code&gt;&lt;/a&gt; overlay bits — PATH, CONTROLS, TANGENTS_IN, TANGENTS_OUT — and switches interpolation modes live. Unlike &lt;a href=&#34;https://github.com/VisualComputing/p5.tree&#34;&gt;&lt;code&gt;createCameraTrack&lt;/code&gt;&lt;/a&gt;, which applies its result to a camera automatically, &lt;code&gt;PoseTrack&lt;/code&gt; produces an interpolated pose you fold into the transform stack yourself with &lt;code&gt;applyPose(track.eval(out))&lt;/code&gt;.&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 track
let uiTrack, uiViz
const out = { pos: [0, 0, 0], rot: [0, 0, 0, 1], scl: [1, 1, 1] }

function setup() {
  createCanvas(600, 400, WEBGL)

  // ── PoseTrack ────────────────────────────────────────────────────────────
  track = createPoseTrack()
  track.add({ pos: [-220, -40,   0], rot: { axis: [0, 1, 0], angle:  0         } })
  track.add({ pos: [ -70, -40,  80], rot: { axis: [0, 1, 0], angle:  Math.PI/3 } })
  track.add({ pos: [  70, -40, -80], rot: { axis: [0, 1, 0], angle: -Math.PI/4 } })
  track.add({ pos: [ 220, -40,   0], rot: { axis: [0, 1, 0], angle:  Math.PI/2 } })

  track.play({ loop: true, bounce: true, duration: 90 })

  // ── Transport panel ──────────────────────────────────────────────────────
  uiTrack = createPanel(track, {
    x: 10, y: 10, width: 130,
    title: &amp;#39;PoseTrack&amp;#39;, info: true,
    reset: false, camera: null,
    color: &amp;#39;white&amp;#39;,
  })

  // ── Path viz &amp;#43; interp modes ─────────────────────────────────────────────
  uiViz = createPanel({
    path:        { value: true },
    controls:    { value: true },
    tangentsIn:  { value: true },
    tangentsOut: { value: true },
    posInterp:   { type: &amp;#39;select&amp;#39;, value: &amp;#39;hermite&amp;#39;,
                   options: [{ label: &amp;#39;hermite&amp;#39;, value: &amp;#39;hermite&amp;#39; },
                             { label: &amp;#39;linear&amp;#39;,  value: &amp;#39;linear&amp;#39;  },
                             { label: &amp;#39;step&amp;#39;,    value: &amp;#39;step&amp;#39;    }] },
    rotInterp:   { type: &amp;#39;select&amp;#39;, value: &amp;#39;slerp&amp;#39;,
                   options: [{ label: &amp;#39;slerp&amp;#39;, value: &amp;#39;slerp&amp;#39; },
                             { label: &amp;#39;nlerp&amp;#39;, value: &amp;#39;nlerp&amp;#39; },
                             { label: &amp;#39;step&amp;#39;,  value: &amp;#39;step&amp;#39;  }] },
  }, {
    x: 460, y: 10, width: 130, labels: true,
    title: &amp;#39;Path viz&amp;#39;, color: &amp;#39;white&amp;#39;,
  })

  noFill()
  strokeWeight(1.2)
}

function draw() {
  background(&amp;#39;#0a0a0e&amp;#39;)
  orbitControl()

  stroke(70, 90, 110)
  grid({ size: 400, subdivisions: 8 })

  // live interp modes — read once per frame
  track.posInterp = uiViz.posInterp.value()
  track.rotInterp = uiViz.rotInterp.value()

  // path overlays — each bit in its own ambient stroke colour
  const { PATH, CONTROLS, TANGENTS_IN, TANGENTS_OUT } = p5.Tree
  if (uiViz.path.value())        { stroke(&amp;#39;#e8ecf1&amp;#39;); trackPath(track, { bits: PATH,         marker: null }) }
  if (uiViz.controls.value())    { stroke(&amp;#39;#4a5566&amp;#39;); trackPath(track, { bits: CONTROLS,     marker: null }) }
  if (uiViz.tangentsIn.value())  { stroke(&amp;#39;#5cd0ff&amp;#39;); trackPath(track, { bits: TANGENTS_IN,  marker: null }) }
  if (uiViz.tangentsOut.value()) { stroke(&amp;#39;#ff6ec7&amp;#39;); trackPath(track, { bits: TANGENTS_OUT, marker: null }) }

  // per-keyframe markers — default six-axis cross oriented by the keyframe&amp;#39;s pose
  stroke(180)
  trackPath(track, { bits: 0 })

  // interpolated object at the track cursor
  track.eval(out)
  push()
  applyPose(out)
  stroke(&amp;#39;#ffd166&amp;#39;)
  noFill()
  box(42)
  pop()
}

const mouseWheel = () =&amp;gt; false

        &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;h1 id=&#34;four-keyframes-one-object&#34;&gt;Four keyframes, one object&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;createPoseTrack&lt;/code&gt; returns a &lt;code&gt;PoseTrack&lt;/code&gt; — a renderer-agnostic state machine for &lt;code&gt;{ pos, rot, scl }&lt;/code&gt; keyframes. &lt;code&gt;track.add(spec)&lt;/code&gt; appends one; adjacent duplicates are skipped by default:&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
