Jak již bylo zmíněno dříve, funkce init() nastaví příkazem setInterval(animate,100), aby se funkce animate() volala pravidelně každých 100ms. Funkce animate() dělá vlastní animaci. Nejprve získá ukazatel ctx na kreslící prostor realizovaný pomocí tagu <canvas>. Dále vymaže kreslící prostor a nakreslí na něj krychli v její okamžité pozici. Nakonec body krychle pootočí pomocí matice rotace rot, tj. vezme matici points, kde jsou v řádcích uloženy souřadnice vrcholů krychle, a vynásobí ji zprava maticí rot, viz rovnice (2). Konkrétní kód funkce animate() je následující:
function animate() { var canvas = document.getElementById("canvas"); // Ziskej odkaz na nas <canvas> tag, kam budeme kreslit. if (canvas.getContext) { // Tohle je tady jen proto, aby prohlizec, ktery nepodporuje <canvas> tag, nedelal nic. var ctx = canvas.getContext("2d"); // ctx je promenna pomoci, ktere lze k nasemu canvasu pristupovat. ctx.lineJoin = "round"; // Nastav, aby spojeni dvou car bylo zaoblene. ctx.clearRect(0,0,150,150); // Vymaz canvas. draw_cube(ctx); // Nakresli krychli. points = points.multiply(rot); // Otoc body v poli points pomoci matice rotace rot. } };
Dále si rozebereme, co přesně dělá funkce draw_cube(), která vykreslí krychli podle souřadnic vrcholů v matici points. Funkce projde postupně všechny stěny krychle, rozhodne, které z nich jsou viditelné, a ty potom vykreslí pomocí funkce draw_face(). Kód funkce je následující:
function draw_cube(ctx) { var i,j,d,e,s; // pomocne promenne var normal = Vector.create([0,0,0]); // promenna pro normalovy vektor ke stene for(i=0;i<6;i++) { // pro kazdou stenu udelej: normal = points.row(normals[i][0]); normal = normal.subtract(points.row(normals[i][1])); // zjisti normalovy vektor. v = points.row(faces[i][0]); // Vezmi libovolny bod steny v = v.add(shift); // pricti posun od oka pozorovatele v = v.toUnitVector(); // udelej z vektoru v a normal vektory delky 1 normal = normal.toUnitVector(); s = v.dot(normal); // spocti skalarni soucin normaloveho vektoru a vektoru od pozorovatele k bodu steny. if (s<0) { // pokud je zaporny, je stena videt. draw_face(ctx,i,s); // tak ji nakresli. Parametr s se jeste hodi na stinovani, proto je predan funkci draw_face. } } };V těle funkce je cyklus probíhající přes všech šest stěn krychle, indexované proměnnou i. Na začátku cyklu je spočítá normálový vektor normal k i-té stěně pomocí pole normals. Dále vezmeme libovolný bod na i-té stěně a najdeme vektor v směřující od oka pozorovatele do tohoto bodu. Nyní pomocí vektorů v a normal zjistíme, jestli je i-tá stěna vidět či nikoli tímto způsobem. Uděláme z obou vektorů vektory délky
Nakonec si vysvětlíme, co dělá funkce draw_face(). Tato funkce má tři parametry: ukazatel na kreslící prostor ctx, pořadové číslo stěny f a skalární součin s, který jsme spočítali ve funkci draw_cube(). Pomocí čísla stěny f a pole faces funkce draw_face() zjistí, které vrcholy stěna f obsahuje. Tyto vrcholy promítne do roviny pomocí perspektivní projekce funkcí perspective_projection(). Každá stěna má svojí barvu. My ji trochu upravíme pomocí skalárního součinu s tak, aby byla jasnější, pokud je k nám stěna více nakloněná a tmavší, pokud méně. Nakonec stěnu f vykreslíme, tj. vyplníme barvou a obtáhneme černou čárou. Konkrétní kód funkce draw_face() je následující:
function draw_face(ctx,f,s) { var i; for(i=0;i<4;i++) { // pro kazdy bod steny f spocitej perspektivni projekci vert[i] = perspective_projection(points.row(faces[f][i])); } s = 0.5*(1-s); // preskalovani s, aby barvy nebyly tak tmave. ctx.fillStyle = "rgb(" + Math.floor(s*colors[f][0]) + "," + Math.floor(s*colors[f][1]) + "," + Math.floor(s*colors[f][2]) + ")"; ctx.beginPath(); ctx.moveTo(vert[0][0],vert[0][1]); ctx.lineTo(vert[1][0],vert[1][1]); ctx.lineTo(vert[2][0],vert[2][1]); ctx.lineTo(vert[3][0],vert[3][1]); ctx.closePath(); ctx.fill(); // Vyplni ji barvou. ctx.stroke(); // Obkresli ji cernou carou (nefunguje v IE). };
Rostislav Horcik 2009-01-04