Cogwheels with Raphaël – part 2

Teething In this article we will grow teeth on our cogwheel. We start with a wheel having 12 teeth and we double that number. As explained in the last article, we have function getPath() dedicated to generating the path in string format. To animate teething we generate the new path and then morph:

teeth = 12;
paths = new Array();
paths[1] = getPath(180, 180, 120, 12, 0);
paths[2] = getPath(180, 180, 120, 24, 0);

180, 180 is the centre point, 120 is the radius, and 12 & 24 are number of teeth. The function to morph is:

function morph()
{
	teeth = 36 - teeth;
	big.animate({path:paths[teeth/12]},5000); 
}

As teeth oscillates between 12 and 24, the path is set to either paths[1] or paths[2]. 5000 is the duration of the animation in milliseconds. We are ready to go. Press Morph!


Oops, something went wrong. Looks like when we double or halve the number of teeth, Raphaël is messing up the points on the path that are for teeth and the points for the holes. A point is a point, Raphaël has no concept of what the points are for. The best solution to this I could think of is to give the 12 teeth cogwheel twice as many points, the second half all being identical to the last of the first half, if that makes sense. The cogwheel will still look the same, but when morphed to a 24 teeth cogwheel the number of points is identical and hence the allocation of points to teeth and holes is the same.

In the code that builds the path we add four dummy points for every tooth we are planning to add. This is because every tooth consists of two half circles, one convex and one concave, each requiring a start and an end point:

/* add extras */
extras = 4 * extras;
while (extras--)
{
	path = path +" L " +(cx+x1)+","+(cy+y1);
}

This should do the trick. Or does it? Click Morph below. It works great for one tooth, and for the 12/24 teeth cog the holes stay intact. But it still doesn’t look right.


What we can do is add one tooth at the time. After all, this seems to work. Click on Morph to see this in action.


How is it done? We use 13 paths, one for each new tooth. Then we animate from one path to the next. Since we are using the same paths over and over again in this little app it makes sense to store them in an array.

paths = new Array();
paths[12] = getPath(180, 180, 120, 12, 12);
paths[13] = getPath(180, 180, 120, 13, 11);
paths[14] = getPath(180, 180, 120, 14, 10);
paths[15] = getPath(180, 180, 120, 15, 9);
paths[16] = getPath(180, 180, 120, 16, 8);
paths[17] = getPath(180, 180, 120, 17, 7);
paths[18] = getPath(180, 180, 120, 18, 6);
paths[19] = getPath(180, 180, 120, 19, 5);
paths[20] = getPath(180, 180, 120, 20, 4);
paths[21] = getPath(180, 180, 120, 21, 3);
paths[22] = getPath(180, 180, 120, 22, 2);
paths[23] = getPath(180, 180, 120, 23, 1);
paths[24] = getPath(180, 180, 120, 24, 0);

The last two parameters passed to getPath() are the number of actual and dummy teeth. See how the number of teeth – actual plus dummy – is constant. These variables could easily be set in a loop, or be calculated when needed. In our case it makes sense to store the paths. In real life, once the number of teeth has been reduced to the final count – in our case 12, be should then get rid of the dummy points in the path with:

cog.attr({path: getPath(180, 180, 120, 12, 0)});

Time to take a look at the morph() function.

function morph()
{
	if (teeth == 12)
		grow(); /* grow teeth */
	else
		lose(); /* lose teeth */
}
function grow()
{
	if (teeth < 24)
	cog.animate({path:paths[++teeth]},500,grow); 
}
function lose()
{
	if (teeth > 12)
	cog.animate({path:paths[--teeth]},500,lose); 
}

Depending on the number of teeth the cogwheel has at the moment, we will either grow teeth or lose teeth. The grow() function animates the path to the one with one more tooth, the lose() function animates the path to the one with one less tooth. See how both function use the animate method with the callback argument, calling itself once one step of the animation had finished. Only until we have reached the intended number of teeth – 24 or 12 respectively.

Adding one tooth at a time is great, but why don’t we try and add the teeth in between the existing teeth? Then we only need two paths to morph between.


While we animate the growing teeth so elegantly, the wheel rolls from side to side as well. And why not?

paths = new Array();
paths[1] = getPath(170, 180, 120, 12, 12);
paths[2] = getPath(440, 180, 120, 24, 0);
 
function morph()
{
	teeth = 36 - teeth;
	cog.animate({path:paths[teeth/12], rotation:teeth*7.5},8000,'<>'); 
}

Let’s finish this article with a bit of fun.


One comment

  1. Patric

    Nice! I’m really impressed of your math skills. Was googeling “cogwheels maths” and ended up here for a while :)

    I’m also very faschinated by algorithms and coding in general and I was actually working with the raphaël library last week. I made a spider/radar diagram :)

    Anyways, just wanted to say hi and thanks

Post a comment

You may use the following HTML:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>