10.6.14

Cartesian Fingers

I was working on a mobile app that has a special type of events-tracking calendar chart, which needed to be visible and interactive at all times, and which can have up to 140 days. 

That's a lot of days to display on a 7-10 inch screen. 

In addition, each day has several fields and there must be enough space for the user to enter stuff in these fields. In other words, no inch can be squandered.

Ideally, the calendar would be a torus, or better yet a simple 2D ring, an inner and an outer circle drawn on a Cartesian plane, and each day would be a segment of the 2D ring. I say ideally because this is how these charts are usually illustrated in my client's books. However, the chart in the app  must be interactive.

Example chart with 8 segments


Problem: As the user adds new days to the chart, the number of days on the ring will grow so therefore either the diameter must grow bigger or the segments must grow smaller.

If the diameter grows bigger, then some of it will become obfuscated on the limited screen real estate of mobile devices. On the other hand, if the segments grow smaller, then the user might not be able to select a particular day, which would be frustrating if the user could view and expand only 1 day at a time.

But if the user could view and expand 5 days at a time: 2 ahead and 2 behind the selected day, then things wouldn't be so bad because (2*Pi/140)*5 = 0.22, which means that even in a chart with the maximum number of days where individual segments might be too small to select, five neighbouring segments still make a decent arc and the user is more likely to see the day they wish to see.

Even if the user accidentally selects a segment slightly off the 5 segments displayed, they can still quickly and easily retrieve the segment they want by scrolling through the neighbouring ones left and right.

The average chart has more days than this one.


This display is not only functional but it is also very similar to my client's actual logo, which is a bonus.


The implementation should be easy: each segment has an arc of length 2Pi/(number of days). When a user clicks on the chart it is easy to retrieve their x and y coordinates and if (x=0,y=0) is the point at the centre of the ring, then we can easily turn the x and y coordinates into polar coordinates using trigonometry because:


(θ, t) are the polar coordinates equivalent to the cartesian coordinates (x, y) obtained in the following manner:


θ = atan2(y,x) 

t^2 = x^2 + y^2  => t = sqrt( x^2 + y^2 )

Both the radius of the small circle (r) and the radius of the big circle(R) are static. The arc length and the number of arcs are not but they known : arc-length = 2Pi/#of days;    #of arcs  =  #of days; so the starting and ending point of each segment is also known.

So a simple pseudo code would read like this:

//draw ring
draw inner circle
draw outer circle

//for each segment in array of segments add start and end
Segments{start: startingAngle, end: endingAngle}

//obtain the polar coordinates from cartesian coords of the click
θ = atan2(y, x);
t = sqrt(abs((x*x) + (y*y)));

//check to see if cartesian coords of the click are within ring
if(t > r && t < R)
{
//convert from (pi, -pi) to (0, 2Pi)
if(θ < 0){θ +=2*Pi}

        //check to see which segment the angle θ is in
for (var i=0; i < Segments.length; i++)
{
if (θ >Segments[i].start && θ < Segments[i].end) {print("Boom! You're in sector "+j);}
}

}
else
{
print(Moob. Finger is not on ring);
}

And in an ideal world where everyone cares for the sanity of Mathematics lovers this works perfectly. But we don't live in an ideal world as illustrated by this funky meme I made especially for all JS lovers.


To be fair, this is not just JS Canvas arc's vision. This is how arcs behave in many scripting languages.

And it wouldn't have mattered if it wasn't for the fact that different segments may have different colours so orientation of the drawing does matter.

The problem was reduced to either hacking up the atan2 function to calculate the reflection or hacking up the segment drawing function to display the segments in the opposite rotation - the one that atan2 expects.

The obvious choice was to alter the mathematically finicky drawing function and the result is demoed below. First day is always red, the rest may vary. Also, currently it's set to accept any number of days but it will only display days between 1 and 140.