<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.2">Jekyll</generator><link href="http://localhost:4000/feed.xml" rel="self" type="application/atom+xml" /><link href="http://localhost:4000/" rel="alternate" type="text/html" /><updated>2025-03-04T22:09:37+01:00</updated><id>http://localhost:4000/feed.xml</id><title type="html">Gerben Jan Hettinga</title><subtitle>Graphics engineer. </subtitle><entry><title type="html">Seiler Points</title><link href="http://localhost:4000/graphics,/bezier/2025/03/04/seiler-points.html" rel="alternate" type="text/html" title="Seiler Points" /><published>2025-03-04T14:54:34+01:00</published><updated>2025-03-04T14:54:34+01:00</updated><id>http://localhost:4000/graphics,/bezier/2025/03/04/seiler-points</id><content type="html" xml:base="http://localhost:4000/graphics,/bezier/2025/03/04/seiler-points.html"><![CDATA[<p>I stumbled upon <a href="https://www.cemyuksel.com/research/seilers_interpolation/">this interesting paper</a> that demonstrates a new way to evaluate Bézier curves using additional points which are called Seiler points. Then using this points and just three linear interpolations the curve can be evaluated. By storing the Seiler points instead of the middle two Bézier control points it becomes an efficient way to evaluate the curves.</p>

<p>Given a cubic Bézier curve with control points \(b_0\), \(b_1\), \(b_2\) and $b_3$ the Seiler points \(s_1\) and \(s_2\) can be computed in the following way: \(s_1 = 3 b_1 - b_0 - b_3,\) and \(s_2 = 3 b_2 - b_3 - b_0.\) Then a curve evaluation \(C(t)\) boils down to:</p>

\[b_{03} = \texttt{lerp}(b_0, b_3, t),\]

\[s_{12} = \texttt{lerp}(s_1, s_2, t),\]

\[C(t) = \texttt{lerp}(b_{03}, s_{12}, (1-t)t)\]

<p>Geometrically this can be interpreted in the following way:</p>

<p><img src="/images/seilers.png" alt="Taken from https://www.cemyuksel.com/research/seilers_interpolation/" /></p>

<p>\(b_{03}\) is a point on \(b_0b_3\) and \(s_{12}\) is point on \(s_1s_2\). Then the final evaluated point is another linear combination between those points. From this it seems that evaluating with Seiler points is akin to evaluating a bilinear surface. With this it seems possible to efficiently evaluate a Bezier curve in shaders. We can use the quadrilateral \(b_0b_1s_2s_1\) as your bilinear surface. Then given your pixel position \(p\) <a href="https://iquilezles.org/articles/ibilinear/">invert</a> it to obtain bilinear coordinates \(u\) and \(v\). 
With this we can easily check if we are evaluating a point on the curve because only on the curve it holds that:</p>

<p>\(t = u\) and \((1-t)t = v.\)</p>

<p>Then we can substitute and check if</p>

<p>\((1-u)u = v\).</p>

<p>However, chances are not very likely for that to happen for any given pixel so we need to add some \(\epsilon\) to this comparison. We can make it so:</p>

\[d = \texttt{abs}(v - (1-u)u)\]

<p>then we can check whether \(d &lt; \epsilon\) to determine if we are close enough to the curve. The effect of this is shown in the following ShaderToy:</p>

<iframe width="640" height="360" frameborder="0" src="https://www.shadertoy.com/embed/lXcSzM?gui=true&amp;t=10&amp;paused=true&amp;muted=false" allowfullscreen=""></iframe>

<p>Unfortunately, this does not give the result that one wants. The thickness is not uniform along the curve and for inflected curves it seems to dissapear altogether. This seems to be because we are evaluating based on the \(uv\) coordinates of the Seiler quad. Still, it seems to be useful in drawing the area under (or above) the curve as checking whether \(v-(1-u)u &lt; 0\) will give this quite nicely.</p>]]></content><author><name></name></author><category term="graphics," /><category term="bezier" /><summary type="html"><![CDATA[I stumbled upon this interesting paper that demonstrates a new way to evaluate Bézier curves using additional points which are called Seiler points. Then using this points and just three linear interpolations the curve can be evaluated. By storing the Seiler points instead of the middle two Bézier control points it becomes an efficient way to evaluate the curves.]]></summary></entry><entry><title type="html">B-spline basis functions</title><link href="http://localhost:4000/graphics/2024/02/01/bspline-functions.html" rel="alternate" type="text/html" title="B-spline basis functions" /><published>2024-02-01T21:52:34+01:00</published><updated>2024-02-01T21:52:34+01:00</updated><id>http://localhost:4000/graphics/2024/02/01/bspline-functions</id><content type="html" xml:base="http://localhost:4000/graphics/2024/02/01/bspline-functions.html"><![CDATA[<p>My tool of choice to investigate, learn about and toy with splines has always been GeoGebra, because it is easy to drag control points and sliders around to see what happens. Splines like B'ezier curves are easily created there as long as you remembered the basis functions by heart. Luckily these basis functions are easy to come by:</p>

\[B_i^k(t) = {k\choose i} (1-t)^{k-i} t^i,\]

<p>here \(k\) is the degree and \(i\) the index so that the i-th basis function is multiplied with the $i$-th control point.</p>

<p>When considering B-splines or even non-uniform rational B-splines (NURBS) this becomes a little harder to do. Using these in GeoGebra requires evaluating the B-spline through De Boor’s algorithm:</p>

\[N_{i, 0}(t) = \begin{cases} 1 &amp; t_i \leq t &lt; t_{i+1} \\ 0 &amp; \text{otherwise} \end{cases}\]

\[N_{i, k}(t) = \frac{t - t_i}{t_{i+k} - t_i} N_{i, k-1}(t) + \frac{t_{i+k+1} - t}{t_{i+k+1} - t_{i+1}} N_{i+1, k - 1}(t)\]

<p>As anyone who has used any of the newer (browser-based) Geogebra versions knows, inputting formulas is an excruciatingly painful ordeal. Moreover, with higher degree splines come increasing levels of recursion, and therefore unbearable slowness in GeoGebra. For this I usually resorted to not use the recursion based formulation of the basis functions but their closed form expressions. Again, for higher degrees this becomes a very error prone and tedious process.</p>

<p>For this purpose I made this simple Python script that can be used expand and write out the B-spline basis functions given a degree \(k\), a knot vector \(\mathbf{t} = [t_0, \ldots, t_{2(k + 1)}]\), the index \(i\) (with respect to the knot vector) and a knot span \(j\) (meaning \([t_j, t_{j+1}]\)):</p>

<pre><code class="language-Python3">from sympy import symbols, simplify
import numpy as np

T = symbols('t')

def N(i, k, knots, span):
   if(k == 0):
      if(i == span):   
         return 1
      else:
         return 0
   
   a = 0
   D_1 = knots[i + k] - knots[i]
   if(D_1 != 0):
      a = (T - knots[i]) / D_1

   b = 0
   D_2 = knots[i + k + 1] - knots[i+1]
   if(D_2 != 0):
      b = (knots[i + k + 1] - T) / D_2

   return a * N(i, k - 1, knots, span) + b * N(i + 1, k - 1, knots, span)
</code></pre>

<p>Then outputting the uniform cubic B-spline basis functions on the interval \([0, 1]\) is as easy as:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>knots = np.array([-3, -2, -1, 0, 1, 2, 3, 4])
span = 3
print(simplify(N(0, 3, knots, span)))
print(simplify(N(1, 3, knots, span)))
print(simplify(N(2, 3, knots, span)))
print(simplify(N(3, 3, knots, span)))
</code></pre></div></div>
<p>Which returns:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-(t - 1)**3/6
t**3/2 - t**2 + 2/3
-t**3/2 + t**2/2 + t/2 + 1/6
t**3/6
</code></pre></div></div>]]></content><author><name></name></author><category term="graphics" /><summary type="html"><![CDATA[My tool of choice to investigate, learn about and toy with splines has always been GeoGebra, because it is easy to drag control points and sliders around to see what happens. Splines like B'ezier curves are easily created there as long as you remembered the basis functions by heart. Luckily these basis functions are easy to come by:]]></summary></entry><entry><title type="html">Triangle indexing</title><link href="http://localhost:4000/graphics/2023/09/20/triangle-indexing.html" rel="alternate" type="text/html" title="Triangle indexing" /><published>2023-09-20T15:54:34+02:00</published><updated>2023-09-20T15:54:34+02:00</updated><id>http://localhost:4000/graphics/2023/09/20/triangle-indexing</id><content type="html" xml:base="http://localhost:4000/graphics/2023/09/20/triangle-indexing.html"><![CDATA[<p>Consider an evenly subdivided triangle with a resolution r which will have a triangular number \(\frac{r(r+1)}{2}\) of points. For instance a triangle with \(r=2\) will have 3 points, \(r=3\) will have \(6\) points, and for \(r = 4\) it will have \(10\) points and looks like this:</p>

<p><img src="/images/triangle.svg" alt="Triangle subdivision." /></p>

<p>Every point on the subdivided triangle can be indexed using two integers \(x\) and \(y\). \((0, 0)\) will give you the bottom-left point, \((r-1, 0)\) and \((0, r-1)\) will give you the bottom right point and top point, respectively.</p>

<p>Say that we are given some barycentric coordinates \(\lambda = [u, v, w]\). We can find out exactly which sub-triangle \([(x_0, y_0), (x_1, y_1), (x_2, y_2)]\) we are currently in. First we  find out which cell of the grid we are in by using \(i = \lfloor r * v \rfloor\) and \(j = \lfloor r * w \rfloor\). Then we can determine in each cell wether we are in the upper triangle by checking whether \((v \mod 1/r + w \mod 1/r) &gt; 1/r\).</p>

<p>Then depending on the if we are in the upper the triangle or the lower we can find the coordinates of the triangle points. If we are in the upper triangle then the triangle is \([(i + 1, j), (i + 1, j + 1) (i, j + 1)]\) or in the lower \([(i, j), (i + 1, j) (i, j + 1)]\). We can even calculate local barycentric coordinates in those triangles by taking \(\lambda_l = \text{fract}(r * \lambda)\) in the upper triangle we need to take \(1 - \lambda_l\) to have the coordinates in the right order.</p>

<p>Consider now that the positions (or other attributes) of the subdivided triangle points are stored in a single one dimensional array. The positions are stored in such a way that the points are inserted starting from the left bottom row of the triangle and moving to the right. If a row ends we go up a row and start again going left to right until the very top of the triangle.</p>

<p>For ease of acces it would be great if we can directly go from a coordinate \((x, y)\) to some index into the array. It seems easy right? Just some arithmetic with the \(x\), \(y\) and \(r\) parameters. Here is what I came up with some time ago:</p>

\[idx(i, j) = i + (j * (2 + r - (j + 1) / 2)) - j - ((j + 1) \% 2) * (j / 2)\]

<p>Yes, this is correct and, no, I do not remember exactly how I determined this.</p>

<p>After many years I thought I should revisit this a bit and find out if I can find something smarter and more concise than this.</p>

<h1 id="can-we-do-better">Can we do better?</h1>

<p>Clearly the index is going to be \(x\) plus the sum of number of points in the rows up untill row \(y\). So what is the sum of the previous rows at \(y\)? it is \(\sum^{y - 1}_{i=0} (r - i)\). Now computing this sum for every coordinate does not improve upon the monstrosity of before. So let’s write it out the sum for different values of \(y\) and try to find a closed form expression:</p>

\[\begin{array}{|l|l|l|}
\hline
  \text{y} &amp; \sum^{y - 1}_{i=0} (r - i) &amp; \text{simplified} \\ 
\hline
   1 &amp; r &amp; r \\
\hline
   2 &amp; r + (r - 1)&amp; 2*r - 1  \\
\hline
   3 &amp; r + (r - 1) + (r - 2) &amp; 3*r - 3  \\ 
\hline
   4 &amp; r + (r - 1) + (r - 2) + (r - 3) &amp; 4*r - 6 \\
\hline
   5 &amp; r + (r - 1) + (r - 2) + (r - 3) + (r - 4) &amp; 5*r - 10 \\
\hline
   6 &amp;  r + (r - 1) + (r - 2) + (r - 3) + (r - 4) + (r - 5) &amp; 6*r - 15 \\ 
\hline
\end{array}\]

<p>Clearly, there is some pattern there! We can substitute the factor before the \(r\) by \(y\), but what about the minus term? It seems familiar because we have seen it before. It is exactly the triangle numbers we used to determine the number of points in a subdivided triangle. Thus the expression can be simplified to</p>

\[\sum^{y - 1}_{i=0} (r - i) = y*r - \frac{y(y-1)}{2}.\]

<p>and the index function then becomes simply:</p>

\[idx(i, j) = i + j*r - \frac{j(j-1)}{2}.\]

<p>Here you can see it in action on shadertoy:</p>

<iframe width="640" height="360" frameborder="0" src="https://www.shadertoy.com/embed/wljyRm?gui=true&amp;t=10&amp;paused=true&amp;muted=false" allowfullscreen=""></iframe>

<p>in this example the above procedures are used to interpolate colours defined at the points of triangle subdivision. In this way you can have a texture defined on a triangle without specifying texture coordinates.</p>]]></content><author><name></name></author><category term="graphics" /><summary type="html"><![CDATA[Consider an evenly subdivided triangle with a resolution r which will have a triangular number \(\frac{r(r+1)}{2}\) of points. For instance a triangle with \(r=2\) will have 3 points, \(r=3\) will have \(6\) points, and for \(r = 4\) it will have \(10\) points and looks like this:]]></summary></entry></feed>