UPS is the bane of my existence. Not only did I have to deal with their low quality API at work (it took me three months of back and forth with their customer service to get the API running), but they also lost the PCBs that I needed for my smarthome project. I couldn’t keep working on it while waiting for the replacements, so I decided to look for something to fill out the time.
three.js is a really impressive framework that I wanted to get into for a long time now. My smarthome will need a dashboard, sooner or later. There’s no reason not to fill out two needs at once, so I started to build a 3D dashboard.
Step 1: Figure out what you want.
This took me a few days of toying around with three.js. I went through a tutorial about it to go from “Oh hell, I’ll never figure this out” to “This ain’t that hard”: Rendering some random 3D model in a basic scene. Et voilà, I had 3D in my browser.
Seeing the 3D model and being able to navigate around it was quite cool. The thing is, I have a 3D screen. The next logical step therefore was to combine the two and render the model in actual 3D, not the pseudo 3D seen on a 2D screen. It’s actually pretty simple: Just use two cameras to render the scene from two slightly different perspectives next to each other (your eyes respectively) and let the 3D screen show it in 3D. I only implemented side-by-side and top-and-bottom, but there are a bunch of other formats that can be used too to generate 3D graphics.
Now back to the question: What’s the plan? Well, me being a huge fan of Iron Man, I had to go into that direction. Iron Man’s HUD is basically a 3D dashboard with a bunch of data over the real world. Not that complicated actually. The way the elements are positioned though (radial around the user), that requires more thinking.
Step 2: First UI elements
But first let’s start by adding a simple element: A wireframe cube. The only purpose it serves it being a point of reference and looking cool. The cube is directly in front of the user and helped me figuring out problems with positioning.
Texts. Dashboards are mostly texts with a bunch of pretty graphs added here and there. This was actually quite simple, as three.js already provides a way to add text to a scene. I only had to write a wrapper to add functions like setText to update text or setPosition to (later) set the position more easily.
The third kind of element I added was the percentages with arcs around them. They’re just there to display percentage of any kind, may it be download progress or some energy meter.
Step 3: Camera
I already implemented the 3D view as my second test with three.js, so this wasn’t a problem. I only had to add a way to toggle between the three modes as well as using the mouse to look around. The cool thing is, it’s actually pretty easy to onclick-fullscreen a canvas to give it the feeling of a normal 3D game.
Step 4: Radial coordinates
Oh boy, that was annoying. three.js uses cartesian coordinates, but I wanted to use radial coordinates to specify the distance from the user as well as the horizontal and the vertical angle. That way it’d be easy to position the elements in a ball around the user. I’m leaving out the math parts here, but after modifying a few equations and flipping a few axes, I finally got it working. The next problem was aligning the elements. I wanted to print out lines of text below each other while having it aligned to the left, right or center. After many tried I figured the easiest way was: Position the elements on their spot, rotate them towards the user, grab their dimensions, reposition according to their dimensions and alignment.
Step 5: Other elements
The black background was quite boring, so I added a skybox. A skybox is a box around the scene with images on the inside to give the impression of a flawless background. On my dashboard, that’s the stars. There’s a lot of tutorials out there for this (some using a newer three.js version than others) and quickly done.
It’s a dashboard. Dashboards have pretty graphs. Graphs are just a bunch of lines from a data point to another, with some labels on their axes. Pretty simple. I draw the axes, print texts as labels at the right spots and draw a line from each data point to the next. The only “problem” was that three.js is pretty barebones, doesn’t support newline characters and only prints “?” instead. But that was quickly fixed, just split the texts on the newline characters and print out the rest of the line below the previous one.
I sometimes see some images on dashboards. Adding images isn’t that hard: Either draw a 2D rectangle out of two triangles (which some sources on the internet suggest), or add a simple 3D cube with the depth being 0 (which is what I did). I couldn’t figure out how to properly draw the triangle-rectangle and decided to simply drop the graphic onto a cube and called it a day.
All in all the result is cool and fun to play with, but not useful. Sure, the dashboard is working, but using it for my smarthome isn’t viable – it’s not scalable for more complex overviews. As I’m using wrappers to work with text and other UI elements, the code isn’t that easy to extend. Other parts of the code (mainly the index.html) aren’t thought out too well as well, as they were written in quite a hurry.
But I seriously consider rewriting it in a suitable way for my smarthome. The hard math is already done and can be reused, and most of the code works and just has to be recycled. The only problem left is the lack of interesting data that can be displayed, but I’m sure that solves itself with each extension of my control system.