Tuesday, March 29, 2011

Developing for the Chumby - Federation Clock v0.1

Technorati Tags: ,,

FederationClock

I had been trying to learn ActionScript and figured the best place to start was to create a simple analog clock but found it very frustrating. Most of the examples for analog clocks are in fla format which requires Adobe tools to create and cost quite a bit. I sometimes question why Chumby Industries went with Flash when they are so focused on providing Open Source products. After much frustration I was able to get the job done and it came out quite nicely. Hopefully someone will find this example useful and save them some frustration.

BTW: I am impressed that there really is not much code to write to get this to work. Most of my frustration came with trying to get the hands of the clock to rotate about a point other than (0, 0). One would think that Adobe would have made it so you could set a registration point on an object but they did not for some reason. They did provide the matrix class which allows you to transform an object to get the same effect.

Source code for this project is at http://www.teelsys.com/Chumby/Clock_0_1.zip.

Project Files

FederationClock001First let’s take a look at the files in the project.

The bin folder contains the compiled swf file. The library folder contains the images used to create the clock. The FederationClock.jpg file is the background of the clock. The other three image files are the hands of the clock. Notice that the color of the files in the FlashDevelop Project Explorer are blue color verses the FederationAnalogClock.jpg file in the root of the project. The blue color indicates that they are part of the assembly and will be included in the final assembly (swf file).

The src folder contains the util folder which contains source files which are included with Chumby project template. The src folder also contains the Main.as which has the main code for the project and will be discussed below.

Finally the FederationAnalogClock.jpg file is a 80x60 thumbnail image to be used when adding the widget to the Chumby either through the Chumby website or on a USB flash drive.

Main.as

FYI: I got some ideas and source code from various sources and have included those in the comments of the source code.

I am sure that this code can be improved greatly and I have some ideas on ways that it can be improved. I have commented on some things which can be improved and I may incorporate those changes at a later time. The biggest improvement that can be made to this code is to create a new class which extends the MovieClip class and adds a registration point to allow the MovieClip to rotate around a point other than (0, 0). This would allow this code to be applied to many more things beyond a simple clock.

Variable Declarations

The first thing I need to comment about is that the key words for private, public, and static must be used differently in ActionScript than in other languages or I am not seeing something with how they are used. I had an issue with the Point types as the values were lost between function calls unless I used the static keyword. I will need to look into the scope of ActionScript variables a bit more so I can understand what the differences are and why I needed to use the static keyword.

private var parent:MovieClip;
private var clockBackground:MovieClip;
private var hourHand:MovieClip;
private var minuteHand:MovieClip;
private var secondHand:MovieClip;
static var hourRotatePoint:Point;
static var minuteRotatePoint:Point;
static var secondRotatePoint:Point;
static var hourCurrentAngle:Number;
static var minuteCurrentAngle:Number;
static var secondCurrentAngle:Number;

There are three basic groupings for the variables. The first are the MovieClip variables for the background and the hands of the clock. The second group are the Point variables for holding the registration points of the clock hands and the last group are number variables for recording the current angle of the hands.


init()

The init function is used to place the background and the clock hands in the widget as well as initializing the registration points and the current angle of the clock hands. Finally the init function calls the clock_onEnterFrame so the clock will show the current time on start.

function init()
{
// Add images to the app
clockBackground = parent.attachMovie("library.FederationClock.jpg", "clockBackground", parent.getNextHighestDepth());
hourHand = parent.attachMovie("library.hourHand_mc.png", "hourHand", parent.getNextHighestDepth());
minuteHand = parent.attachMovie("library.minuteHand_mc.png", "minuteHand", parent.getNextHighestDepth());
secondHand = parent.attachMovie("library.secondhand_mc.png", "secondHand", parent.getNextHighestDepth());

// Position the hands to 12:00:00 position
hourHand._x = (Stage.width - hourHand._width)/2;
hourHand._y = (Stage.height/2) + 8 - hourHand._height;
minuteHand._x = (Stage.width - minuteHand._width)/2;
minuteHand._y = (Stage.height/2) + 8 - minuteHand._height;
secondHand._x = (Stage.width - secondHand._width)/2;
secondHand._y = (Stage.height / 2) + 35 - secondHand._height;

// Pin Location (Registration Point)
hourRotatePoint = new Point(hourHand._x + (hourHand._width/2), hourHand._y + 86);
minuteRotatePoint = new Point(minuteHand._x + (minuteHand._width/2), minuteHand._y + 113);
secondRotatePoint = new flash.geom.Point(secondHand._x + (secondHand._width/2), secondHand._y + 107);

hourCurrentAngle = 0;
minuteCurrentAngle = 0;
secondCurrentAngle = 0;

// Show the current time
clock_onEnterFrame();
}

rotateAroundPoint

The rotateAroundPoint function rotates a MovieClip around the registration point by a number of degrees. One thing to remember is if the MovieClip is already rotated, it will be rotated from the current position so the final angle will be the current rotation plus the angle passed into the function. I had not thought of this and when my code started working it produced an interesting effect with the seconds going faster and faster.


I want to thank Ryan Bosinger for posting this code on his blog. It helped a great deal although I could not get it to work at first. The reason it was not working is the scope of the variables as I mentioned above.

// FROM: http://ryanbosinger.com/blog/2008/actionscript-30-rotate-around-center-point/
static function rotateAroundPoint (mc:MovieClip, angleDegrees:Number, ptRotationPoint:Point)
{
var m:Matrix=mc.transform.matrix;
m.tx -= ptRotationPoint.x;
m.ty -= ptRotationPoint.y;
m.rotate (angleDegrees*(Math.PI/180));
m.tx += ptRotationPoint.x;
m.ty += ptRotationPoint.y;
mc.transform.matrix = m;
}

Main

The Main function is the constructor for the Main class. I just took the default name for the class and I think this makes things a bit confusing as I had thought this function was the starting point of the application however it is not.


The Main function initializes the parent variable, runs the init function, and assigns the onEnterFrame event handler for the parent MovieClip. (OK, I don’t know why I am assigning it using the mc variable rather than the parent variable. They are the same instance so it works.)

function Main(mc:MovieClip)
{
// save reference to main movieclip
this.parent = mc;

init();

mc.onEnterFrame = clock_onEnterFrame;
}

onMenuSelect

Dead code which was from the Chumby project template.

function onMenuSelect(i:Number) {
trace("select " + i);
}

main

The entry point for the application.

static function main(mc:MovieClip)
{
var app = new Main(mc);
}

clock_onEnterFrame


The clock_onEnterFrame function is the event handler which handles the onEnterFrame event. This is the function which drives the application.


The function gets the current time and determines how many degrees each of the hands should be at in order to display the time. To reduce some processing time, the angle required to display the time is compared to the current angle of the hand. If they are the same then the rotation code is not called. The frame rate for the project is 12 fps so I assume this is called 12 times per second. I suppose it may be more efficient to change the frame rate for the project to 1 fps.


If the angles are different then the hand is rotated back to the original position then moved to the correct angle. I am sure there is a better way to do this so if you do it differently please comment on it.

function clock_onEnterFrame()  {
var dt:Date = new Date();
var hrs:Number = dt.getHours() + dt.getMinutes() / 60 + dt.getSeconds() / 3600;
var mns:Number = dt.getMinutes() + dt.getSeconds() / 60;
var scs:Number = dt.getSeconds();

//set hour hand
var angle:Number = hrs * 30;
if (angle != hourCurrentAngle)
{
rotateAroundPoint(hourHand, -hourCurrentAngle, hourRotatePoint);
rotateAroundPoint(hourHand, angle, hourRotatePoint);
hourCurrentAngle = angle;
}

//set minute hand
angle = mns*6;
if (angle != minuteCurrentAngle)
{
rotateAroundPoint(minuteHand, -minuteCurrentAngle, minuteRotatePoint);
rotateAroundPoint(minuteHand, angle, minuteRotatePoint);
minuteCurrentAngle = angle;
}

//set second hand
angle = scs * 6;
if (angle != secondCurrentAngle)
{
rotateAroundPoint(secondHand, -secondCurrentAngle, secondRotatePoint);
rotateAroundPoint(secondHand, angle, secondRotatePoint);
secondCurrentAngle = angle;
}
}

Conclusion


I hope you have found this useful. Please if you have any ideas or comments on this code, please post them below.


If you look for this clock on the Chumby site you will not see it there. I felt there are too many clocks to choose from and this one is no different and no better than the others listed on the site. I have some ideas to create a clock which utilizes the built-in alarm clock and/or shows world times. If I do create something more useful along those lines, I will add them.


You are free to modify the code presented here to create your own clock. Have fun and post any comments you may have regarding this code or any other posts on this blog.

No comments:

Post a Comment