# Having fun with Html5 Canvas (2)

In the previous part we’ve used Html5 Canvas to draw lines and circles with custom fill and stroke styles.

In this part, we will make our widget interactive by letting the user customize our gears setup.

# Part II: Rendering two interlocked gears

Our objective is to let the user control the position of the second gear. At the same time, we want the two gears to remain interlocked and synchronized.

Here is the first draft:

```
Gear.prototype.connect = function (x, y) {
var r = this.radius;
var dist = distance(x, y, this.x, this.y); // Calculate the distance between two gears
// To create new gear we have to know the number of its touth
var newRadius = Math.max(dist - r, 10);
var newDiam = newRadius * 2 * Math.PI;
// The number of teeth must be an integer:
var newTeeth = Math.round(newDiam / (4 * this.connectionRadius));
// Make new gear
var newGear = new Gear(x, y, this.connectionRadius, newTeeth,
this.fillStyle, this.strokeStyle);
// Adjust new gear's rotation to be in direction oposite to the original
var gearRatio = this.teeth / newTeeth;
newGear.angularSpeed = -this.angularSpeed * gearRatio;
return newGear;
}
```

We can now hook this up to Canvas Mouse-Move event,

```
var gear2 = gear.connect(3 * (W / 4), H / 2);
// Helper function to translate (x,y) to coordinates relative to the canvas
function getMousePos(canvas, evnt) {
var rect = canvas.getBoundingClientRect();
return {
x: evnt.clientX - rect.left,
y: evnt.clientY - rect.top
};
}
canvas.onmousemove = function (evnt) {
var pos = getMousePos(canvas, evnt);
var x = Math.min(0.7 * W, Math.max(0.3 * W, pos.x));
var y = Math.min(0.7 * H, Math.max(0.3 * H, pos.y));
gear2 = gear.connect(x, y);
}
setInterval(function () {
canvas.width = canvas.width;
gear.render(context);
gear2.render(context);
}, 20);
```

Mouse-over to activate

This is a step in the right direction, but not everything is right yet. Gears are not interlocked, and each is doing its own thing.

Here are the two problems we must address:

`gear2.radius`

differs from the`newRadius`

we’ve calculated. This is since we had to keep the number of teeth round.- Rotation of
`gear2`

is not synced up with the first gear.

To take care of (1) we must let our new gear change it’s actual position to allow to have both the number of teeth it desires and let it be interlocked with the first gear.

```
Gear.prototype.connect = function (x, y) {
var r = this.radius;
var dist = distance(x, y, this.x, this.y);
// To create new gear we have to know the number of its touth
var newRadius = Math.max(dist - r, 10);
var newDiam = newRadius * 2 * Math.PI;
var newTeeth = Math.round(newDiam / (4 * this.connectionRadius));
// Calculate the ACTUAL position for the new gear,
// that would allow it to interlock with this gear
var actualDiameter = newTeeth * 4 * this.connectionRadius;
var actualRadius = actualDiameter / (2 * Math.PI);
var actualDist = r + actualRadius; // Actual distance from center of this gear
// Angle between center of this gear and (x,y):
var alpha = Math.atan2(y - this.y, x - this.x);
var actualX = this.x + Math.cos(alpha) * actualDist;
var actualY = this.y + Math.sin(alpha) * actualDist;
// Make new gear
var newGear = new Gear(actualX, actualY, this.connectionRadius, newTeeth,
this.fillStyle, this.strokeStyle);
// Adjust new gear's rotation to be in direction oposite to the original
var gearRatio = this.teeth / newTeeth;
newGear.angularSpeed = -this.angularSpeed * gearRatio;
return newGear;
}
```

Mouse-over to activate

This adjustment alone took care of an annoying effect where we could have place a gear inside another gear, but it was not enough to sync-up the two.

Now we must make the gears interlock at the point of connection:

```
this.phi0 = alpha; // At time t=0, rotate this gear to be at angle Alpha
newGear.phi0 = alpha + Math.PI + (Math.PI / newTeeth);
// At the same time (t=0), rotate the new gear to be at (180 - Alpha), facing the first gear,
// And add a half gear rotation to make the teeth interlock
newGear.createdAt = this.createdAt; // Also, syncronize their clocks
```

This will do the trick. The downside of this method, however, is that we are changing `this`

. It means that any other gears that were previously synced up with `this`

are no longer synced.
Every time `this.phi`

is updated by some delta, by how much `newGear.phi`

should be updated?
The answer is `delta * (newGear.angularSpeed / this.angularSpeed)`

as it is the ratio between rotation speeds of the gears.
Knowing this, we can update both gears by `delta = (this.phi0 - alpha)`

, cancelling the effect on this:

```
// At time t=0, rotate this gear to be at angle Alpha
this.phi0 = alpha + (this.phi0 - alpha); // = this.phi0, This does nothing.
newGear.phi0 = alpha + Math.PI + (Math.PI / newTeeth) +
(this.phi0 - alpha) * (newGear.angularSpeed / this.angularSpeed);
// At the same time (t=0), rotate the new gear to be at (180 - Alpha),
// facing the first gear, and add a half-a-tooth rotation to make the teeth interlock
newGear.createdAt = this.createdAt; // Also, synchronize their clocks
```

This way, `this.phi0`

remains unchanged, while the other gear syncs-up to it:

Mouse-over to activate

Source code: part2.js