GSoC Coding Phase - Part 2
Last time we left off at where I’d implemented some code to display plots in a Jupyter notebook. The next task was to get an independent y-axis working in the Line Graph. I looked at other plotting frameworks’ implementation of this feature. What matplotlib did was that it had different kinds of plots added to an axes, ad the axes was then plotted. To get independent axes, it was twinned with one of the axes. For example if you have a primary axis and twin a secondary axis with it, they will be plotted opposite to each other. ggplot required you to explicity set the scale for the secondary axis relative to the primary axis. After some thinking I decide that having a primary and secondary axis with series of data being added to either one of them would be a good options. The scaling would be done automatically of course. But how would you differentiate between a plot on either axis? A simple solution for now could be to make the secondary plot lines dashed, so that’s what I went with. Remember I had told you that setting up the tests in the beginning helped me with a bug? While implementing multiple y axes, I gave negative input points for the first time and noticed that I hadn’t though of this while implementing the logic. I could easily check the difference in old and new plots by using git. If the newly rendered image was different it would show up as updated in git. I had lifted the logic directly from Graph-Kit the android library I’d made and it seems I’d made that mistake there too. So I spent some time rectifying that.
The next task was to implement Bar Graph. This seemed like an easy enough task as I distinctly remembered it to be less work even when implementing it in Graph-Kit. Bu this time I didn’t repeat the same mistake as I did with Line graph. Although I did initially lift the logic again I immediately checked for loopholes such as negative coordinates. I had to rewrite some of the code to get that done. Additional features planned for Bar Graph were plot hatching and stacked plots. Hatching is the patterns you fill inside the bars, something like shading the bars. For implementing hatching there were two options. One was to use primitives to generate the patterns. As this would be implemented using primitives everything could be done using Swift. This would make all the renderers support hatching. The other option was to have an enum/list of possible hatching patterns and pass a unique value for each case to the renderer and all the pattern generation will be handled independently by the renderer. This meant that each renderer implementation would need to have some logic to generate the patterns, and maybe some renderers would not have out of the box support for this. The former had some disadvantages. If we generated all the pattersn in Swift using primitives we’d have to handle the edge cases i.e when half of a pattern was inside the rectangle and half of it outside, we’d have to clip it. Then there would also be the logic for drawing the pattern in the bound area of the rectangle. Basically we’d have to reinvent the wheel. Even if this was done, in the case of SVG, a lot of statements to render primitives would be added to the file. This would take a lot of time to parse and therefore delay the final image generation. Both the renderers I was using had the pattern generation feature built right into them. They would easily handle all edge cases and filling the pattern in the required area. So I went with the second option. I just had to implement some logic to generate the shapes to be used in the hatching pattern. The patterns I implemented were forward slash backward slash, hollow circle and grid to name a few. Then I went on to implement stacked bars. This could be used when multiple series of data were to be plotted on the came graph. The next series of data was placed over or below the previous series’ bar depending on whether the data point was positive or negative. There were no specific challenges to this other than the usual debugging one does to get any feature working. The last part left in Bar Graph was to allow horizontal bars too. I just had to switch the x and y values if the graph was to be plotted horizontally.
I went on to implement Scatter Plot. This wasn’t a very difficult task as the logic was pretty much the same as that of Line Graph, the difference being that the line weren’t to be joined by lines and instead be replaced with shapes. I added functions to the renderers to draw triangles and polygons. Generating the points to draw shapes like triangle, hexagon, stars was simple trigonometry and rotation of points. I also added a helper function to rotate points about a center. This wrapped up Scatter Plot.
While implementing Bar Graph, the plot had to accept String input also. So I had to add String variables to the Point type. This raised a question. Was the current way of using Point to accept data the correct way? We had to think of other ways to support other data types such as Double, Int, etc. Is generics the right way to proceed? This is a topic that’s currently under discussion.
Meanwhile I started work on a python free implementation to display plots in Jupyter Notebooks. I also simultaneously fixed the plot dimensions issue with the AGG Renderer. I’ll discuss this next time, so stay tuned!
Enjoy Reading This Article?
Here are some more articles you might like to read next: