This summer was the Processing Foundation’s seventh year participating in Google Summer of Code. We received 90 applications, a significant increase from previous years, and were able to offer 16 positions. Over the next few weeks, we’ll be posting articles written by some of the GSoC students, explaining their projects in detail. The series will conclude with a wrap-up post of all the work done by this year’s cohort.
I spent this summer working on p5.js alongside Aidan Nelson, with the help of our mentors, Stalgia Grigg and Kate Hollenbach. My tasks for the summer included implementing missing 2D primitives in WebGL mode of p5.js: arc, point, bezierVertex, curveVertex, quadraticVertex, and text.
My first contribution to p5.js was when I was writing my GSoC proposal. This was just a minor change in reference examples for curves and some examples for vertex(). These small changes helped me understand the use of grunt, lint, and other node modules being used in the build process. I was really happy (and surprised) when my proposal got accepted 😄. I was going to get to contribute to one of my favorite libraries! We had a shared branch on github where we would compare our pull requests to be merged.
When I started the summer, I’d already been using p5.js for some simple sketches and animations, watching Daniel Shiffman’s videos, but I hadn’t really dived into the codebase. I started by going through manual examples, debugging them in Chrome, and watching the code jump from function to function. I have to admit that it was a little daunting at first. My first task was to implement arc for WebGL mode, and so I decided to go through an already implemented ellipse and debug it in the chrome editor.
The code started to make sense, and I started to make small changes so as to move forward in implementing arc. Before my first evaluation, I was able to complete my implementation of arc #2942, with all three modes (OPEN, PIE, CHORD), and add documentation and test examples. Ellipse was now refactored by using the same arc implementation. This closed issue #2181.
WebGL by default renders square points. Looking through some resources on the web gave me an idea of discarding pixels from a square point further to render a circular point. Adding the shader in p5.js was relatively simple. But upon adding the shader I came across a devilish bug that wouldn’t be fixed, no matter what I tried. I spent a week and a half going through every portion of the code, looking for where I went wrong, but I was stuck. I decided to contact Stalgia and he looked through the code and suggested I have a look at my shader code. There was one thing in my shader that was different from all the other shaders in p5.js. It was using an attribute variable for pointSize while p5.js used uniform. I changed the variable to uniform and that was it! Everything now worked perfectly.
One small problem was still there though. A good approach to rendering points would be to use the default square point when the size of the point was really small. When it becomes big enough, it should render good looking circular points. I posted this issue on Github and a contributor who had been working on this before, suggested to use his shader which worked the same way as mentioned. Finally, I opened a pull request for point with two test examples for the primitive. This closed issues #2662 and #2184.
BezierVertex, CurveVertex, and QuadraticVertex
I decided to go for curves next. I went through a great article on bezier curves for this task. Reading the article I came to know that a bezier curve can be easily converted to Catmull Rom Splines, which meant that if I was able to implement bezierVertex, implementing curveVertex would be relatively simple (I’d just have to convert the points). QuadraticVertex can be implemented by changing the formula used to compute the coefficients for curves.
I finally added the required functions and started testing curves. Everything worked quite well, except one. Filling curves gave weird results. At first, I thought maybe there was something wrong going on in my code but a further debugging revealed that curves cannot be filled with all the intersections and loops without a proper triangulation algorithm. After approval from Kate and Stalgia, I started testing different triangulation libraries (Earcut, Libtess, Tess2). Earcut was the most precise but wasn’t giving correct results. At first we thought tweaking the library would help, then I decided to contact Earcut’s creator. I sent him an email and he responded suggesting that Earcut wouldn’t be a good option for us.
Libtess seemed to work well because it was based on the same GLU Tesselator implementation that was being used in Processing. The only problem with Libtess was that it was quite bigger than Earcut. We contacted Lauren McCarthy to see if it was okay to integrate this library for the triangulation part. She encouraged us by saying that we can add the library as a node module, similar to how OpenType was being used for 2D text in p5.js.
After integrating Libtess, I decided to improve the code for some performance gains. I was able to implement a look-up table so we wouldn’t need to compute coefficients after rendering curve. This was a huge performance boost from the previous implementation. All that was left was to submit a pull request with an example showcasing what you can do with WebGL curves in p5.js. This closed issues #2185, #2186, and #2187.
While working on curves I was also able to fix a minor issue with triangle stroke in 2D in p5.js. I also ported some examples from Processing to p5.js #242. Now I had one big task left: Implementing text for p5.js WebGL mode. Let me just mention one thing though: “One does not simply implement text in WebGL.” 😥
I tried several different methods for rendering text in WebGL. At the end I was able to pick two that seemed perfect for rendering text. The first one was Signed Distance Field Rendering. This was to use a 2D texture as text. But this wasn’t a simple texture that would contain information about the glyph pixel. Rather it would contain the information about the distance to the border of the glyph, which gives us the ability to render text in different sizes. But there were still some problems (you can read a detailed discussion here).
At the end we decided to go for a GPU based rendering approach, which is a relatively new technique for rendering text. The overhead of rendering text goes to the GPU instead of the CPU, and we get sharp, crisp text at any size. I wasn’t able to implement text since a major contributor had already done most of the work for text using the same technique. We decided to pull that implementation and polish it further, looking for bugs and improvements, instead of writing everything from scratch.
A full list of my contributions on the webgl-gsoc-2018 branch can be seen here.
That wraps up my summer with Processing Foundation. I learned a lot this summer, and working with the great community at Processing was a memorable experience. I plan on still contributing to the library in my spare time. 😃