1var IS_SKV8 = typeof document == "undefined";
2var HAS_PATH = typeof Path2D != "undefined";
3var HAS_DISPLAY_LIST = typeof DisplayList != "undefined";
4
5var NumTeeth = 24;
6var NumGears = 60;
7var DeltaTheta = Math.PI/90;
8var FaceColors = ["#000099", "#006600", "#990000", "#EEEE00"];
9var SideColors = ["#0000FF", "#009900", "#FF0000", "#CCCC00"];
10
11function makeGear(pathLike, r) {
12  var dT = Math.PI*2/NumTeeth;
13  var dTq = dT/4;
14  var outer = r;
15  var inner = 0.7 * r;
16  pathLike.moveTo(Math.sin(-2*dTq)*outer, Math.cos(-2*dTq)*outer);
17  for (var i=0; i<NumTeeth; i+=2) {
18    pathLike.lineTo(Math.sin(dT*i-dTq)*outer, Math.cos(dT*i-dTq)*outer);
19    pathLike.lineTo(Math.sin(dT*i+dTq)*inner, Math.cos(dT*i+dTq)*inner);
20    pathLike.lineTo(Math.sin(dT*(i+1)-dTq)*inner, Math.cos(dT*(i+1)-dTq)*inner);
21    pathLike.lineTo(Math.sin(dT*(i+1)+dTq)*outer, Math.cos(dT*(i+1)+dTq)*outer);
22  }
23}
24
25function gearPath(r) {
26  if (HAS_PATH) {
27    p = new Path2D();
28    makeGear(p, r)
29    p.closePath();
30    return p;
31  } else {
32    return null;
33  }
34}
35
36function gearDisplayListStroke(r, color) {
37  if (HAS_DISPLAY_LIST) {
38    p = new Path2D();
39    makeGear(p, r)
40    p.closePath();
41    var dl = new DisplayList();
42    dl.strokeStyle = color;
43    dl.stroke(p);
44    dl.finalize()
45    return dl;
46  } else {
47    return null;
48  }
49}
50
51function gearDisplayListFill(r, color) {
52  if (HAS_DISPLAY_LIST) {
53    p = new Path2D();
54    makeGear(p, r)
55    p.closePath();
56    var dl = new DisplayList();
57    dl.fillStyle = color;
58    dl.fill(p);
59    dl.finalize()
60    return dl;
61  } else {
62    return null;
63  }
64}
65
66function strokeGear(ctx, gear) {
67  if (HAS_PATH) {
68    ctx.stroke(gear.path);
69  } else {
70    ctx.beginPath();
71    makeGear(ctx, gear.r);
72    ctx.closePath();
73    ctx.stroke();
74  }
75}
76
77function fillGear(ctx) {
78  if (HAS_PATH) {
79    ctx.fill(gear.path);
80  } else {
81    ctx.beginPath();
82    makeGear(ctx, gear.r);
83    ctx.closePath();
84    ctx.fill();
85  }
86}
87
88function draw3DGear(ctx, angle, gear) {
89  ctx.strokeStyle = gear.sideColor;
90  ctx.fillStyle = gear.faceColor;
91  ctx.rotate(angle);
92  strokeGear(ctx, gear);
93  for (var i=0; i < 20; i++) {
94    ctx.rotate(-angle);
95    ctx.translate(0.707, 0.707);
96    ctx.rotate(angle);
97    if (HAS_DISPLAY_LIST) {
98        ctx.draw(gear.gearStroke);
99    } else {
100        strokeGear(ctx, gear);
101    }
102  }
103  if (HAS_DISPLAY_LIST) {
104      ctx.draw(gear.gearFill);
105  } else {
106      fillGear(ctx, gear);
107  }
108  ctx.rotate(-angle);
109}
110
111function draw3DGearAt(ctx, angle, gear) {
112  ctx.save();
113  ctx.translate(gear.x, gear.y);
114  draw3DGear(ctx, angle, gear);
115  ctx.restore();
116}
117
118var onDraw = function() {
119  var ticks=0;
120  var rotation = 0;
121  var gears = [];
122
123  for (var i=0; i<NumGears; i++) {
124    color = Math.floor(Math.random()*FaceColors.length);
125    r = Math.random()*100+5;
126    gears.push({
127        x: Math.random()*500,
128        y: Math.random()*500,
129        path: gearPath(r),
130        gearFill: gearDisplayListFill(r, FaceColors[color]),
131        gearStroke: gearDisplayListStroke(r, SideColors[color]),
132        r: r,
133        faceColor: FaceColors[color],
134        sideColor: SideColors[color]
135    });
136  }
137
138  function draw(ctx) {
139    ctx.resetTransform();
140
141    ctx.fillStyle = "#FFFFFF";
142    ctx.fillRect(0, 0, 499, 499);
143
144    rotation += DeltaTheta;
145    if (rotation >= Math.PI*2) {
146      rotation = 0;
147    }
148
149    for (var i=0; i < gears.length; i++) {
150      gear = gears[i];
151      draw3DGearAt(ctx, rotation, gear);
152    }
153
154    ticks++;
155    if (IS_SKV8) {
156      inval();
157    }
158  };
159
160  function fps() {
161    console.log(ticks);
162    ticks = 0;
163    setTimeout(fps, 1000);
164  };
165
166  setTimeout(fps, 1000);
167
168  return draw;
169}();
170
171if (!IS_SKV8) {
172  window.onload = function(){
173    var canvas = document.getElementById("gears");
174    var ctx = canvas.getContext("2d");
175    function drawCallback() {
176      onDraw(ctx);
177      setTimeout(drawCallback, 1);
178    }
179    setTimeout(drawCallback, 1);
180  }
181}
182
183console.log("HAS_PATH: " + HAS_PATH);
184