AdaFractal Part1: Ada with a Portable GUI
by Rob Tice –
The AdaFractal project is exactly what it sounds like; fractals generated using Ada. The code, which can be found here , calculates a Mandelbrot set using a fairly common calculation. Nothing fancy going on here. However, how are we going to display the fractal once its calculated?
To Graphics Library or not to Graphics Library?
We could use GTKAda, the Ada binding to GTK, to display a window… But that means we are stuck with GTK which really is overkill for displaying our little image, and is only supported on Windows and Linux... And Solaris if you’re into that sort of thing...
The Server Solution
The last piece of the puzzle we need is to be able to serve our web files and fractal image to a web browser for rendering. Since we want to ensure portability, we should probably leverage the inherent portability of Ada and use AWS. No, it’s not the same aws that you may use for cloud storage. This AWS is the Ada Web Server which is a small and powerful HTTP server that has support for SOAP/WSDL, Server Push, HTTPS/SSL and client HTTP. If you want to know more about the other aws, check out the blog post Amazon Relies on Formal Methods for the Security of AWS. For the rest of this blog series, let’s assume AWS stands for Ada Web Server.
Because AWS is written entirely in Ada, it is incredibly portable. The current build targets include UNIX-like OS’s, Windows, macOS, and VxWorks for operating systems and ARM, x86, and PPC for target architectures. Just by changing the compiler I have been able to recompile and run this application, with no source code differences, for QNX on 64 bit ARM, macOS, Windows, and Linux on 64 bit x86, and VxWorks 6.9 on 32 bit ARM. That’s pretty portable!
For those who have access to GNATPro on Windows, Linux, or macOS, you can find AWS included with the toolsuite installation. For GNAT Community users, or GNATPro users who would like to compile AWS for a different target, you can build the library from the source located on the AdaCore GitHub.
Hooking up the pieces
Hooking up the fractal application to AWS was pretty easy. It only required creating a URI router which takes incoming GET requests and dispatches them to the correct worker function. The full router tree can be found in the router_cb.adb file.
function Router (Request : AWS.Status.Data) return AWS.Response.Data is URI : constant String := AWS.Status.URI (Request); Filename : constant String := "web/" & URI (2 .. URI'Last); begin -- Main server access point if URI = "/" then -- main page return AWS.Response.File (AWS.MIME.Text_HTML, "web/html/index.html"); -- Requests a new image from the server elsif URI = "/fractal" then return AWS.Response.Build (Content_Type => AWS.MIME.Application_Octet_Stream, Message_Body => Compute_Image); -- Serve basic files elsif AWS.Utils.Is_Regular_File (Filename) then return AWS.Response.File (Content_Type => AWS.MIME.Content_Type (Filename), Filename => Filename); -- 404 not found else Put_Line ("Could not find file: " & Filename); return AWS.Response.Acknowledge (AWS.Messages.S404, "<p>Page '" & URI & "' Not found."); end if; end Router;
Here is a sample of a simplified router tree. The Router function is registered as a callback with the AWS framework. It is called whenever a new GET request is received by the server. If the request is for the index page (“/”), we send back the index.html page. If we receive “/fractal” we recompute the fractal image and return the pixel buffer. If the URI is a file, like a css or js file, we simply serve the file. If it’s anything else, we respond with a 404 error.
The AdaFractal app uses a slightly more complex router tree in order to handle input from the user, compute server side zoom of the fractal, and handle computing images of different sizes based on the size of the browser. On the front-end, all of these requests and responses are handled by jQuery. The image that is computed on the backend is an array of pixels using the following layout:
type Pixel is record Red : Color; Green : Color; Blue : Color; Alpha : Color; end record with Size => 32; for Pixel use record Red at 0 range 0 .. 7; Green at 1 range 0 .. 7; Blue at 2 range 0 .. 7; Alpha at 3 range 0 .. 7; end record; type Pixel_Array is array (Natural range <>) of Pixel with Pack;
AWS serves our application to whichever port we specify (the default port is 8080). We can either point our browser to localhost:8080 (127.0.0.1:8080) or if we have a headless device (such as a Raspberry PI, a QNX board, or a VxWorks solution) we can point the browser from another device to the IP address of the headless device port 8080 to view the fractal.
How about the math and performance?
An interesting side experiment came from this project after trying to increase the performance of the application. After implementing a thread pool to parallelize the math, I decided to try using fixed point types for the math. The implementation of the thread pool and the fixed point versus floating point math performance will be presented in Part 2 of this blog post.