2020 International Conference on Computational Science and Computational Intelligence (CSCI)

Cross-Compiled Plotting with SwiftVis2

Nicholas J. Smoker and Mark . Lewis Department of Computer Science, Trinity University, San Antonio, Texas, United States

Abstract— This paper explores adding support to SwiftVis2, Indeed, even many visualization libraries that are not JVM a plotting library written in Scala, for compiling and running based, such as PLplot and ggplot2, are cross-platform. Less across the JVM, web browsers, and native compiled code. common are cross-compile target visualization libraries. To We find that it is possible to use a single core library written elaborate, Scala allows code to be compiled to multiple in standard Scala across these various compile targets with targets; the most common is the JVM, but JavaScript and little additional code. The quality of the output and ability native machine language are also supported, using Scala.js to handle large datasets varies significantly for different and Scala Native respectively. A cross-compile target library implementations. The use of Scala, or other languages has notable advantages: programmers using SwiftVis2 can that have compilers targeting multiple platforms, makes it use the same code to produce plots for native and web possible to easily target many platforms with relatively little applications with only very minor modifications. To make effort. SwiftVis2 a cross-compile target library, we have made changes to allow the backend to be entirely platform agnostic Keywords: Data Visualization, Scala, Cross-Compile (by this, we mean that our backend code has no dependencies on libraries specific to any target). This was enabled by 1. Background and Introduction an early design decision to have the actual drawing per- Visualization of data through plots has a long history that formed through calls to an abstract type we refer to as goes back nearly two thousand years [1]. In the modern a Renderer. The core implementation includes its own era, this work is done with computers and software and as Renderer that outputs SVG graphics as a String. Writing the amount of data and its usages have grown, the need separate Renderer implementations for each compile tar- to visualize it has grown as well. Having the same tools get allows the same core logic to be used for plotting on across multiple platforms for this work can be beneficial each of them. We are currently able to reproduce plots that so that users don’t have to learn different tools for every utilize the full capabilities of SwiftVis2 on every platform, platform that they work with. In the past, this meant that though the implementation on the Native platform lags the tools had to work on various operating systems. A variety others, in large part because that is the least mature backend of tools have been published for just that purpose in various for the Scala compiler. disciplines [2], [3], [4]. Today it can imply working on web browsers and mobile environments as well. Virtual machines, 1.1 JVM like the JVM and .NET, have provided a certain level of OS The original version of SwiftVis2 is described in [7] independence for many years [5], but often have significant and was written to work with SVG and JavaFX on the memory and startup overhead. Just working on the web also JVM. Shortly after the publication of that paper, a second makes many tools OS independent, but runs into limitations Renderer for the JVM using Swing was added to compare of the JavaScript sandbox on browsers. performance and output quality. These comparisons are Plotting tools as a category encompass both user-level discussed in this paper in section 3. The decision to make tools with graphical interfaces for manipulating settings as a Swing Renderer was motivated in large part because well as libraries used with programming languages. In this of the poor performance of the JavaFX based version, but paper, we concern ourselves with the latter category and it is made even more significant by the decision to ex- look at how we can do plotting across multiple compile clude JavaFX from the standard JRE/JDK installs for many targets using a library written in a single language. The distributions, including OpenJDK, beginning with Java 11. goal is for the majority of the functional code to be written This also motivated us to separate the JVM code into three in a platform and compile-target independent manner such sub-projects. One handles JVM-specific functionality that that small additions can be added for each compile-target to is independent of both JavaFX and Swing. This primarily provide functionality there. includes code that uses the java.io package for file SwiftVis2 [6] is a plotting library written in Scala, which, manipulation. There are two other sub-projects that include up to this point, has used JavaFX or Swing for rendering. the specific Renderers for JavaFX and Swing. These Since Scala compiles to the JVM, SwiftVis2, like many other are very small sub-projects, but they enable users to avoid visualization libraries, is cross-platform, meaning that it including graphics elements that they might not have as part will function well regardless of the user’s operating system. of their local installation of the JVM.

978-1-7281-7624-6/20/$31.00 ©2020 IEEE 317 DOI 10.1109/CSCI51800.2020.00061 1.2 Scala.js run the JVM, Scala Native applications can start running far more quickly, which may be a more noticeable factor than Scala.js is an open source Scala to JavaScript compiler overall performance in many use cases. They also use far with optimization, allowing developers to write Scala code less memory in many situations than what the JVM must for web applications. This project is fairly mature, as it allocate on startup. has existed for several years and is used in a number of For our purposes, the most challenging aspect of working professional environments. In February of 2020, the Scala.js with Scala Native has been finding a good rendering solu- project officially came out with version 1.0 [8]. Scala.js is tion. The graphics libraries we have employed are low-level, highly interoperable, allowing the programmer to use any and have made some aspects of plotting more difficult than JavaScript library with their Scala code. It supports both they would be with the JVM or with Scala.js. strong and dynamic typing, with a large standard library and support for IDEs. The JavaScript code produced by Scala.js’ full optimization mode is comparable in performance, and 1.4 Existing Solutions often faster, than handwritten JavaScript code, though it is Though there are several other options for plotting in predictably illegible. It can, however, run up to three times Scala, to our knowledge, there is no other visualization slower than the JVM, and compilation itself can be lengthy. library compiling to the full range of targets now supported Scala.js is expedient for our purposes because it allows by SwiftVis2. Of the noteworthy alternatives, EvilPlot [9] easy use of the HTML5 canvas within Scala code, providing provides support for Scala.js and the JVM, but not for Scala a well-documented, established, and easy to use method Native; Vegas [10] supports neither Native nor Scala.js. The of rendering the plots produced by SwiftVis2’s platform New Scala Plotting Library (NSPL) [11] provides support for agnostic backend using JavaScript. We believe that support all three platforms, but unfortunately lacks any documen- for Scala.js significantly expands the use cases of our library; tation beyond examples. Indeed, a common theme among SwiftVis2 plots could now be used for visualization within a many Scala plotting libraries is a lack of comprehensive web application, or embedded for use with a plot generator documentation: Vegas and the NSPL both lack an API or from user defined collections of data. Scala.js also includes function reference of any kind. directives for outputting code that can be called directly from JavaScript. We have just begun implementing this function- 2. Methods ality. Once in place, users could include the complied “.js” files on a website, and easily add plots with calls to plain 2.1 Achieving Cross-compilation JavaScript. SwiftVis2’s plots are represented as immutable Scala ob- 1.3 Scala Native jects defined by case classes. Each plot is able to render itself by calling methods on the Renderer interface. The core Scala Native provides “an optimizing ahead-of-time com- abstractions of SwiftVis2, and the rendering interface, do not piler and lightweight managed runtime designed specifically rely on external libraries. As a result, we were able to adapt for Scala.” Its first release, version 0.1, was made public them to be platform agnostic with only minimal changes in 2017, and it is currently in version 0.3.9. Like Scala.js, to make SwiftVis2 compatible with the older versions of Scala Native is open source and highly active. The main Scala supported by Scala Native. None of these changes advantages provided by Scala Native are its low-level func- affect performance. After the core of SwiftVis2 had been tionality: full support for pointers and dynamic memory made platform agnostic, we separated SwiftVis2’s various management are present in Scala Native. Much like Scala.js, Renderers into nested projects within SwiftVis2, so that Scala Native also offers a high level of interoperability, users could choose to compile SwiftVis2 with whichever allowing the programmer to call C code within their Scala Renderer they wished to use and ignore the requirements code. This has the advantage of allowing programmers ac- of working with any of the other Renderers. cess to the numerous useful and performant libraries written in C, C++, and even Rust for use with their Scala Native 2.2 Renderers code. It also provides full use of the Scala standard library, most of the Java standard library, and bindings for parts of The different Renderers now present within SwiftVis2 the C and C POSIX standard libraries. all implement the Renderer interface within the platform- Theoretically, Scala Native could eventually provide su- agnostic core. This allows the core of SwiftVis2 to work perior performance to Scala code running on the JVM. In with all of them seamlessly. The various actions needed to its present state, however, Scala Native remains inferior to implement the Renderer interface are fairly simplistic. As the JVM in performance. Compile and linkage times are such, the main challenge of this project has been finding also lengthy for Scala Native. Two areas where Scala Native suitable libraries to call from our Scala code. This has beats JVM applications quite handily are startup times and primarily been an issue of finding rendering platforms with memory consumption; without the overhead of having to bindings for Scala.js and Scala Native.

318 For Scala.js, finding such a platform was simple; the which caused several problems that will be elaborated on HTML5 canvas is an established and well documented when the output of our renderers is reviewed in section 3. rendering platform. Indeed, the Canvas in JavaFX, a platform Another abstraction that SDL2 lacked was robust support which we already supported, was heavily modeled after for rendering text. Bindings did exist to the SDLTrueType the HTML5 Canvas. In addition, bindings for the HTML5 library, which allowed us to at least abstract away some Canvas have already been written for Scala.js. As a result, aspects of the process of rendering text. As we will show the process of implementing a Renderer for use with later, however, this solution was far from robust. Finally, SwiftVis2’s Scala.js platform was mostly smooth. Difficul- many of SDL2’s built-in methods and structs store data ties arose, however, because the existing bindings were not used for drawing in integer form, which, predictably, caused comprehensive; that is, while the bindings covered most of numerous highly noticeable problems with the placement of the methods we needed to call from the HTML5 canvas, elements of our plots. While the issues with text rendering they did not cover all of them. Calling the methods that we may be fixed in time, and improvements could be made to needed to use required us to make use of an interoperability our ellipse drawing solutions, the storage of data as integers feature in Scala.js that allows for dynamic typing within is an inherent limitation of SDL2, which we cannot do Scala code. While this was highly irregular, since Scala is a much about. Luckily, the flexibility of SwiftVis2 means that strongly typed language, and SwiftVis2 attempts to adhere renderers built on different bases, which do not have such to a functional paradigm, it worked well and did not present limitations, may be created later. issues in the rest of our project. Our HTML5 Renderer produced excellent plots, and those plots can be embedded 2.4 Plotting Code within webpages. As was mentioned earlier, the plots themselves are rep- resented by case classes in Scala that form a hierarchical 2.3 Scala Native structure with the various plot elements. All of these ele- Scala Native presented a far more challenging problem. ments are part of the core package. As such, the code for Even in its current, preliminary, state, Native is very inter- building a plot is uniform across all the various compile operable with C and C++ libraries, and bindings already targets. As was discussed in [6], the core provides a facade existed for a multitude of excellent, well documented, and and a literate interface for building these plots. The second established libraries. The comparatively lower level nature of set of plots shown in section 3 used code like the following many of those libraries compared with SwiftVis2, and with for producing the plots. Scala itself, however, meant that creating rendering code for many of them would present significant challenges. Many of val plot = Plot . scatterPlot (x, y, "Sim" , "Radial", "Azimuthal" , r , xSizing = PlotSymbol . Sizing . Scaled , the libraries we considered, for instance, did not have built- ySizing = PlotSymbol . Sizing . Scaled) in methods to draw ellipses, and though we knew that we . updatedAxis [NumericAxis ]( "x" , axis => axis .copy (min = Some( −0.19) , max = Some( −0.14) , tickLabelInfo = could implement our own algorithms for required methods if axis . tickLabelInfo .map(_ . copy ( numberFormat = necessary, we also knew that the effects on performance of "%1.2f")))) . updatedAxis [NumericAxis ]( "y" , axis => axis .copy (min = doing so would most likely be negative. This was particularly Some(100.77) , max = Some(100.72) , tickLabelInfo = concerning given that Scala Native, in its current state, is axis . tickLabelInfo .map(_ . copy ( numberFormat = already known to be slower than the JVM. "%1.2f")))) Ultimately, we elected to implement an SDL2 based Renderer first. We did so for several reasons. Firstly, SDL2 offered more high-level abstractions than some com- This code used the scatterPlot facade method then petitors, such as OpenGL. This allowed us to adapt our adjusted both the x and y axes to have manually set bounds existing methods to the Native platform without requiring and number formatting. sweeping changes to the code. Second, because SDL2’s The rendering code varied slightly for each compile target compilation is fairly simple, we were able to avoid the and graphics library. They looked like the following. significant complications to our compilation pipeline that some alternatives, such as Qt, may have introduced. Thirdly, / / JavaFX SDL2’s bindings were well integrated with Scala Native’s FXRenderer ( plot , 1200, 1000) type system, and as such, working with them would not / / Swing pose any additional difficulties. SwingRenderer ( plot , 1200, 1000) Our decision to use SDL2 did present some challenges. / / SVG Though SDL2 had higher level abstractions available than JVMSVGInterface ( plot , "output / file / path" , 1200, 1000) many of our other choices, it still did not have built in // Scala . js ellipse drawing functionality. This forced us to implement JSRenderer ( plot , canvas ) our own Bresenham algorithm [12] for drawing ellipses,

319 / / SDL2 ( Scala Native) integers in SDL2’s primitives, and the inherent imprecision SDLRenderer ( plot , 1200, 1000) of integer math. Integer math is also the most likely cause of another flaw in the Scala Native renderer. Overlap and misplacement of We strive to make the calls reasonably consistent across histogram bars in the upper left of the plot, where bars the different platforms. Our primary style for easy plot- are close together, is far more noticeable in the Native plot ting is an apply method in a companion object for the than in the output of the other renderers. Here, the small Renderer. This enables the object to be used like a imprecisions created by rounding cause almost every bar to function as shown here. overlap, which, in some cases, results in visually jarring gaps between bars. This is a flaw that, unfortunately, likely cannot be fixed due to the reliance of SDL2’s geometric primitives 3. Results on integers. In all likelihood, another graphics library will To demonstrate the capabilities of the various platforms, be required to create a Scala Native renderer that does not we created two plots using each of the renderers that are have this flaw. currently implemented. The first plot is one that was created A final flaw in the SDL2 renderer is the shape of the for testing SwiftVis2, showing many of the capabilities of ellipses drawn by the renderer. Due to SDL2’s lack of this tool in a single plot with four different regions. This an ellipse or circle primitive or drawing function, we had plot also gives us a good sample to compare the outputs of to implement our own ellipse drawing algorithm based on the various renderers. Bresenham. That algorithm, in addition to being relatively You can see this plot as produced by each of the renderers slow, lacks antialiasing, and since it still relies on SDL2’s in figures 1. Note that much of the data in these plots is primitives and drawing functions, is also limited by integer randomly generated, so the details vary a bit between them. math. For the purposes of this plot, the result of these Most notable here are the versions produced by the ren- limitations is not a deal-breaker, but that does not change derers on different compile targets, the Scala.js and Native the fact that this is a clear limitation, both visually and in versions, shown in figure 1. and 1.E, though there are terms of performance. small differences between the SVG output and that of the Other flaws in the SDL2 renderer include the inability to JVM renderers, particularly in the thickness of the lines adjust line thickness, particularly evident when comparing used to draw the axes, as well as in a small blank region the sinusoidal wave in figure 1.E to that in the other plots. near the center of the bottom right region of the plot. In This is another limitation of the library. Some of the lines general, though, the output of the renderers that were already that form the axes are also colored when they should not implemented is quite consistent. be. This flaw is likely a result of not properly adapting the The output of the Scala.js renderer is consistent with that rendering code we use in other renderers to control color to of the JVM renderers. Indeed, the Scala.js output is rather the SDL2 renderer, and can thus be fixed without changing difficult to distinguish from that of the JVM renderers, with libraries. Many of our limitations, however, are limitations the exception of the text. This includes, in some cases, the of the library. Overall, this result is highly encouraging, flaws present in those renderers. In all renderers, you will and indicates that a Scala Native renderer on par with the notice a tendency for the bars of the histogram in the top left JVM and Scala.js renderers could likely be created using a of the plot to blend together. That is, it appears that not all different library, or even with extensions to SDL2. of the borders between histogram bars render properly. This The second plot that we compare here is one for which seems the least prevalent in JavaFX, but certainly still occurs, SwiftVis2 was initially created as a replacement to the earlier suggesting a flaw in the process used to render histograms SwiftVis package. It shows the particles in a simulation of that causes certain bars to overlap, leaving nowhere for planetary rings drawn to scale so that we can see the features a border to be rendered. With the exception of that flaw, of the system. Figure 2 shows the outputs for the JavaFX, however, the Scala.js renderer produces excellent output that Swing, JavaScript, and Native renderer. We made an SVG, is certainly equal in quality to the JVM and SVG renderers. but it was 882 MB in size because every one of the particles The Native renderer is a different story. An obvious flaw is a line of SVG text making it unacceptable for publication. in the Native renderer is the text placement; text produced In general, SVG is not a good format for these types of plots. by the Native renderer is currently all the same size, and The ring simulation shown here included 8.9 million cannot be restricted to occupy a specific bounds. This is particles. Unfortunately, the JavaScript engine on Chrome largely due to the complexity of rendering text using SDL2, wouldn’t handle more than 6 million points, so we limited which requires use of a locally present truetype font, and the data to the first 6 million particles for all renderers. This manual creation and placement of textures from that font. It limitation doesn’t impact our ability to compare the output. is likely that this problem can be mostly fixed, however, text Unlike the first set of plots, for these plots there are sig- placement may never be perfect because of the prevalence of nificant differences between the JVM renderers. The Swing

320 Fig. 1 THESE ARE FIVE VERSIONS OF OUR "COMPLEX PLOT" MADE WITH THE DIFFERENT RENDERERS.AIS SVG, B IS JAVA FX, C IS SWING,DIS SCALA.JS, AND E IS SCALA NATIVE.

renderer produces the most accurate view of the simulation. it really is. The particles are drawn scaled to their size and they are all sub-pixel. As a result, the anti-aliasing used has a significant The true challenge for rendering this data using JavaScript impact on the quality of the output. JavaFX does not seem was how to get the data into the browser. We implemented to handle sub-pixel geometry well as the image is highly two approaches. One was to have a server send JSON data over-saturated. for the particles in an Ajax request. This method was very slow and ran into memory problems on the browser after a The JavaScript renderer output isn’t as over-saturated at few million particles. The other approach was to convert the the JavaFX version, but it isn’t as good as the Swing renderer binary file to a JavaScript file with a hard coded JavaScript either. It would probably be good enough to work with these array the included all the data. This approach scaled up to 6 datasets, but the extra darkness of these plots does give a million particles and is what is shown here. Unfortunately, false impression of the ring material being more dense than that approach isn’t as flexible. This indicates that plots with

321 Fig. 2 THESE ARE THE FOUR VERSIONS OF A PLOT OF 6 MILLION PARTICLES IN A RINGS SIMULATION.AIS JAVA FX, B IS SWING,CIS SCALA.JS, AND D IS SCALA NATIVE.

really large numbers of individual elements probably aren’t Scala Native 0.4 has already been given a modified garbage well suited for the JavaScript environment. collector that would likely get around these issues, but the Getting this plot in our current native implementation SDL2 binding has not yet been ported to that version so we was even more challenging than for the JavaScript version. couldn’t use it. Reading the data was easier because we were able to make Having 6 million points in a plot also allowed us to get calls to the fread method that mirrors the way these data some idea of the performance of the different rendering files are written in C++. However, the memory manager in engines. Simple timing results are shown in table 1. As Scala Native is not currently as robust as that in the JVM, we found in [7], the JavaFX renderer is surprisingly slow. and the amount of memory used to make this plot challenged This is surprising because the move from Swing to JavaFX the current implementation. To get 6 million particles to was largely motivated by a desire to have a library that draw, we had to comment out significant functionality from could make more complete use of graphics hardware. While the ScatterStyle class that does the rendering. This JavaFX does have 3D graphics support that isn’t in Swing, functionality isn’t used in this particular plot, so it didn’t the performance of its 2D support is remarkably poor for this matter, but the way it is coded involves performing map application. The speed benefits from only drawing single operations on Option types, which were still consuming pixels per particle and making other simplifications to the memory. The sizing of the dots was also producing a large ScatterStyle are clearly visible in the timing results for number of tuples, consuming even more memory. As we SDL2. This should not be taken as any indication of how have no anti-aliasing present in our SDL2 renderer, we fast that tool would be with full functionality if memory simplified the code to draw single pixels for each particle. constraints hadn’t forced those simplifications. This doesn’t alter the output, but it does lead to lower In addition to the time spent rendering, we also show memory consumption and much faster performance. The fact the time spent loading the data. For visualizing large data that the native output in figure 2.D looks so remarkably like sets, it is the sum of these two that really matters in many the JavaFX output in figure 2.A further underscores how applications, but if the data is generated by the application poor the anti-aliasing is in JavaFX. It is worth noting that only the render times would be significant, so we show them

322 Table 1 only falls short when the amount of data involved becomes ROUGH TIMING RESULTS FOR LOAD AND RENDER WITH 6 MILLION larger than the browser environment handles well. For most SCALED DOTS TAKEN FROM A RING SIMULATION.SEE TEXT FOR applications, that isn’t an issue. Looking forward, we intend CAVEAT ON THE NATIVE RENDERER. to explore adding the ability to access the full capabilities of the library from plain JavaScript by simply including a Renderer Load Time [sec] Render Time [sec] JSExport JavaFX 3 130 “.js” script file through appropriate usage of the Swing 3 32 annotations in Scala.js. We are also working on a renderer JavaScript 13 60 that uses the Slinky binding to React[13]. In general, React ∗ SDL2 3 2 doesn’t work well with a Canvas, but it works very well with SVG, so this implementation uses the typed tag bind- ing in Slinky to produce plots that work well with React separately. applications. With continued development of languages like Scala and 4. Conclusions and Future Work Kotlin that can compile to a broad variety of platforms, we expect that it will become increasingly feasible to have At this point, we feel that we have demonstrated the libraries, both for plotting and other purposes, that not only feasibility of plotting across multiple compile targets with run on multiple platforms, but can do so in multiple ways fairly minimal effort using Scala. This same type of cross- on each platform with most of the code only having to be compile target development should be possible in Kotlin written once. This type of capability can speed development and other languages that support multiple targets. We are as it allows developers to target applications to specific needs optimistic that in the next few years, development on Web without having to recreate libraries that they might want to Assembly will advance to the point that languages like use from other target platforms. This is helpful because while Scala and Kotlin can have compiler backends targeting that the JVM runs in many places, it doesn’t run in browsers and platform as well. there are times when the memory overhead and startup time Much work remains to be done to improve rendering of the JVM isn’t acceptable and native executables are a options for Scala Native. Different graphics libraries could better choice. be used to avoid some of the inherent limitations of SDL2, in particular its reliance on integers in its primitives. Bindings References exist for Qt, which, among other advantages, uses doubles [1] M. Friendly and D. J. Denis, “Milestones in the history of the- in many of its primitives and has built in functionality for matic cartography, statistical graphics, and data visualization,” URL drawing ellipses and text. On the other hand, using Qt has the http://www. datavis. ca/milestones, vol. 32, p. 13, 2001. potential to significantly complicate our compilation process, [2] C. H. Grohmann and G. Campanha, “Openstereo: open source, cross- platform software for structural geology analysis,” in AGU Fall which is already somewhat elaborate. Bindings exist for Meeting abstracts, 2010. OpenGL, but as a very low level graphics library, adapting [3] Y. Li, Q. Hu, and S. Gong, “Research on a cross-platform map plotting the high level framework of SwiftVis2’s Renderer to it technique based svg,” Dianguang yu Kongzhi(Electronics, Optics & Control), vol. 18, no. 11, pp. 49–52, 2011. may prove challenging. Ultimately, the creation of more [4] E. Pante and B. Simon-Bouhet, “marmap: a package for importing, Renderers for SwiftVis2 may not result in any one perfect plotting and analyzing bathymetric and topographic data in r,” PLoS option for quite some time, but instead result in several One, vol. 8, no. 9, 2013. [5] J. Xu, “Practical cross-platform charts and graphics with asp .net core different options with distinct strengths and weaknesses. mvc,” 2017. Further improvements can be made to the SDL2 [6] M. C. Lewis, “Swiftvis2 - plotting/visualization with graphi- Renderer. Some of these improvements are simply a cal dataflow analysis.” https://github.com/MarkCLewis/SwiftVis2, ac- cessed: 2018-04-07. matter of fixing problems within our rendering code. Others, [7] M. C. Lewis and L. L. Lacher, “Swiftvis2: Plotting with spark using such as better ellipse drawing and adjustable line thickness, scala,” in International Conference on Data Science (ICDATA ’18), would be best implemented through use of the SDL_gfx vol. 1, no. 1. This paper explores the development of a plotting package for Scala called ..., 2018, pp. 93–99. extension, which provides additional primitives and drawing [8] S. Maintainers, “Announcing scala.js 1.0.0,” https://www.scala-js.org/ functions. Currently no SDL_gfx bindings for Scala Native. news/2020/02/25/announcing-scalajs-1.0.0/, accessed: 2020-04-22. We could either create our own, or simply call the code [9] C. Technologies, “Evilplot: Home,” https://cibotech.github.io/evilplot/, accessed: 2020-04-22. directly from our existing rendering code. Doing so may, [10] A. Fenton, “Vegas - the missing for scala,” https://www. however, serve to further complicate our compilation pro- vegas-viz.org/, accessed: 2020-04-22. cess, and relying heavily on Scala Native’s interoperability [11] I. Bartha, “new scala plotting library,” https://pityka.github.io/nspl/, accessed: 2020-04-22. may put us at the mercy of future changes to a project which [12] E. Andres, “Discrete circles, rings and spheres,” Computers & Graph- is still in its early stages. ics, vol. 18, no. 5, pp. 695–706, 1994. On the JavaScript side, things are much more mature. For [13] S. Laddad, “Slinky - write react apps in scala just like es6,” https: //slinky.dev/, accessed: 2020-06-14. most plotting tasks, the Scala.js Renderer implementation produces perfectly acceptable output. Our implementation

323