My favorites | English | Sign in

Google Desktop APIs (Labs)

Desktop Gadgets: Rotating Objects

This article was written and submitted by an external developer. The Google Desktop Team thanks Teodor Filimon for his time and expertise.


Teodor Filimon, Gadget Developer
August 2007
"Give me a place to stand on, and I will move the Earth!"  -  Archimedes


Introduction

In the physical world, we rely on our eyes to observe all kinds of objects. We perceive the world in three dimensions, but in reality a two dimensional image is projected onto our retina, only to be processed by our brain with spatial perspective. That's why when we see a drawing of a cube on paper, we 'see' a 3D image of what actually is a 2D object.

This article is about rotating elements in a gadget. Links to source code of all the examples discussed here can be found at the end of this article.

How To Understand Rotation

A gadget can be thought of as a sheet of paper, on which the developer is "drawing" his UI (User Interface) elements. Mathematically, there are many ways of specifying a point in a plane. The one most relevant to this article is the polar system, which consists of an axis, an angle, and a length.

What we're interested in is the rotation angle of the image, which is initially zero degrees (no rotation). A positive value will turn the image clockwise around its axis located in the upper-left corner. A negative value will turn it counter-clockwise. An example is shown in Figure 1.

Rotation angles
Figure 1: Examples of rotation values

When we add an <img> object, we usually specify at least two things: the position and the image source. So, it's only natural that the specified coordinates will become the axis of rotation. As I've mentioned previously, this means that each rotation will take place around the upper-left corner of the image, like in Figure 2.

Rotation axis
Figure 2: The default rotation axis

It's important to know that we can do this not only for images, but other types of elements as well since the rotation property is inherited from basicElement.

We can change the rotation axis by setting a value for the pinX and pinY properties, which are also inherited by every UI element. This enables us to achieve certain effects such as clock hand movement or spinning status indicators very easily (Figure 5 and Figure 7). When pinX and pinY are not set, their value is considered zero, so the axis is naturally the point located at the (0,0) position of the element.

But how do we actually rotate objects? The answer comes in the next section of the article.

Rotating UI Elements

Basically, the rotation property of an element can be changed either in the XML or the JavaScript code. An initial angle is usually set in the XML (if it's different from zero), and then the rotation can be set dynamically in the JavaScript code. This example simulates the behavior of a tachometer — you may have seen it in cars measuring engine RPM. :)

//in main.xml

<img name="needle" src="needle.png"
  x="67" y="67" rotation="135" />

//since the needle is horizontal at first,
//and positioned in the center of the gauge,
//we need to put it into the right position
//in main.js

function accelerate(){
  needle.rotation+=1;
  //we can also set a limit
  if (needle.rotation==315){
    needle.rotation=135;
    alert("Engine overheated!");
  }
}

function decelerate(){
  if (needle.rotation>135)
    needle.rotation-=1;
}

Notice how in this example we're taking advantage of the fact that rotation is a read/write property. This allows us to increase or decrease it relative to the current value.

Possible Uses

Here are some more examples of rotation usage in a gadget. Since rotation is so flexible and easy to implement, I'm sure you can come up with many more use cases on your own.

  • Representation of similar elements with different rotations
  • Chart gadget
    Figure 3: Similar elements with different rotations

    The lines I used to draw the graph you see in Figure 3 are actually <div> objects, and they were added using the following snippet of code:

    for (i=0;i>dots.length-1;i++){
      lines[i]=chart.appendElement("<div height='2' background='#888888'/>");
      lines[i].x=(dots[i].x-minXValue)*xUnit+2;lines[i].y=height-(dots[i].y-minValue)*yUnit-2;
      lines[i].rotation=-Math.atan((dots[i+1].y-dots[i].y)*yUnit/(dots[i+1].x-dots[i].x)/xUnit)*180/3.1415;
      lines[i].width=Math.sqrt(sqr((dots[i+1].y-dots[i].y)*yUnit)+sqr((dots[i+1].x-dots[i].x)*xUnit));
    }

    Every <div> is accessible from an element from the lines array, and for readability's sake, the coordinates are set on different lines of code. To match the chart lines perfectly, width and rotation are set using mathematical formulas.

  • Symmetrical buttons
  • Web News gadget
    Figure 4: Opposite functions are usually symbolized by symmetrical images

    In common programs such as web browsers or media players, opposing actions are symbolized by mirror images (Figure 4). Here's a cool tip: a good way to minimize the gadget build size is to have two <buttons> use the same image, but with one of them having a rotation of 180 (degrees). This optimization occurs quite often in the form of extend/collapse buttons (Figure 4). In this case it makes sense to maintain a single button, since both actions can't appear or be performed at the same time. When the button is clicked, we just change the rotation (and position) to suggest the counterpart of the previous action. Here's how I did it:

    function setUpTheDisplay(){
      //...
      theButton.x=93+options("ShowNews")*153;
      theButton.y=(1-options("ShowNews"))*42;
      theButton.rotation=180*(1-options("ShowNews"));
    }

    As you can see from the code, it's easier to have one button in this case behaving as a toggle.

  • Dynamic display of a value which is constantly changing
  • Analog Clock
    Figure 5: Rotation in our day-to-day lives

    The best example for this is a clock, which measures a continually changing value. The only tricky part is calculating the rotation angle based on the time value, and it turns out that it's very easy to solve. We start with the time value which maps to the zero rotation value, so a horizontal hand with a rotation set to zero points to the '3' on the clock. We can then come up with this formula: angle=(clockValue*5-3*5)*6, where 5 is the number of divisions between two values and 6=360 degrees/60 divisions. This is how trigonometry blends with real life. :)

    //in main.xml
    
    ...
    
    <img name="HourHand" pinX="3" pinY="27" src="hourhand.png" x="52" y="52"/>    
    
    <img name="MinuteHand" pinX="3" pinY="40" src="minhand.png" x="52" y="52"/> 
    <img name="SecondHand" pinX="2" piny="40" src="secondhand.png" x="52" y="52"/>
    
    ...

    We set the pin (axis) of our choice, to ensure a proper, realistic rotation.

We can use rotation not only to improve functionality, but visual appeal as well:

An appealing interface induces users to retain your gadget, so why not include some visual effects? As shown in Figure 6, you can easily create some cool effects responding to events like onmouseover, onmouseout, etc. What's shown is the Media Player Remote gadget. If you run the gadget, you'll see that the faceplate is split into two rotating halves, and each is pinned to opposite corners of the player.

Media Player Remote
Figure 6: Visual effects

When your gadget does something which takes some time, you can communicate this to the user by showing an animated status indicator. It can be as simple as a rotating dot.

Status indicators
Figure 7: Activity indicators

Notice that the indicator rotates a single image to create a professional-looking animation.

Conclusion

In this article, I showed you how rotation can improve not only the visual interface, but the functionality behind a gadget as well. It may even provide an opportunity to optimize your code. And even in the 3rd millennium, analog sometimes trumps digital.

Many gadgets, everyone!

Resources


Author Bio

Teodor Filimon

I'm a natural born programmer. My first contact with a techno-gadget was Star Trek (remember those cool sensors?). I used to fill up a whole room with drawings of them when I was only 3 years old. Generally, I find a lot of inspiration for intuitive interfaces in things with "star" in their names (like Star Wars, Stargate,... :-) . I like the border between interface and function — it's the essence of a program. Anyway, I'm a software engineering student now, and you can learn more about me and what I'm thinking and doing at my website or blog.


Creative Commons License
This work is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License.