<!DOCTYPE html>
<html> <!-- Copyright (c) Nematrian Limited 2018 -->
<head></head>
<body>
<a href="HTMLCSSJSTutorial">
<canvas id="SmallSpinningRegularPolyhedra">
HTML / CSS / JavaScript Tutorial
</canvas>
</a>
<script>
var x2 = document.getElementById("SmallSpinningRegularPolyhedra");
x2.setAttribute("width","250");
x2.setAttribute("height","52");
x2.setAttribute("style","border: 1px solid black");
var c2 = x2.getContext("2d");
var shapes = [regularTetrahedron(),
regularCube(),
regularOctahedron(),
regularDodecahedron(),
regularIcosahedron()];
var faceFillStyles = ["rgba(0,127,0,.5)",
"rgba(127,0,0,.5)",
"rgba(0,0,127,.5)",
"rgba(0,80,127,.5)",
"rgba(100,160,80,.5)"];
var edgeStrokeStyles = ["rgba(0,0,0,.5)",
"rgba(0,0,0,.5)",
"rgba(0,0,0,.5)",
"rgba(0,0,0,.5)",
"rgba(0,0,0,.5)"];
var rotAxes = [[1, 0, .7],
[1, 1, 0.3],
[0, 1, .6],
[1, 1, .6],
[1, 0, .7]];
var initialRotAngles = [0, 0, 0, 0, 0];
var xOrigins = [25, 75, 125, 175, 225];
var yOrigins = [35, 35, 35, 35, 35];
var scalings = [8, 8, 12, 8, 8];
var rotSpeeds = [0.1, .3, .11, .12, .16]; // in seconds
var xAxis = [1, 0, 0];
var yAxis = [0, 1, 0];
var c = 0;
var frameRate = 50; // in milliseconds
animate3dShapeOnCanvas(x2, c2, shapes, faceFillStyles, edgeStrokeStyles,
rotAxes, initialRotAngles, xOrigins, yOrigins, scalings,
rotSpeeds, xAxis, yAxis, c, frameRate)
function animate3dShapeOnCanvas(canvas, ctx, shapes, faceFillStyles, edgeStrokeStyles,
rotAxes, initialRotAngles, xOrigins, yOrigins, scalings,
rotSpeeds, xAxis, yAxis, c, frameRate) {
var projectionID = null; // The setInterval() ID value for animation
var i;
var noShapes = shapes.length;
var dynamicRotAngles = new Array;
for (i = 0; i < noShapes; i++) {
dynamicRotAngles[i] = initialRotAngles[i];
}
projectionIncrement();
projectionID = setInterval(projectionIncrement, frameRate);
function projectionIncrement() {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
var i;
for (i = 0; i < noShapes; i++) {
dynamicRotAngles[i] += rotSpeeds[i] * 2 * Math.PI * frameRate / 1000;
ctx.beginPath();
project3dShapeOnCanvas(ctx, shapes[i], xAxis, yAxis, c,
faceFillStyles[i], edgeStrokeStyles[i],
rotAxes[i], dynamicRotAngles[i],
xOrigins[i], yOrigins[i], scalings[i]);
}
ctx.fillStyle = "maroon";
ctx.font = "16px Arial";
ctx.fillText("HTML / CSS / JavaScript Tutorial", 8, 16);
}
}
function project3dShapeOnCanvas(ctx, shape, xAxis, yAxis, c,
faceFillStyle, edgeStrokeStyle,
rotAxis, rotAngle, xOrigin, yOrigin, scaling) {
var unitisedRotAxis = unitiseVector(rotAxis);
var rot = rotationMatrix(unitisedRotAxis, rotAngle);
var n = unitiseVector(vectorCrossProduct(xAxis, yAxis));
//n is unit vector perpendicular to plane formed by xAxis and yAxis
var i, j, x, y, a, ax, ay, az;
var rotatedPoint, rx, ry, rz;
var noVertices = shape[0].length;
var noEdges = shape[1].length;
var noFaces = shape[2].length;
var rotatedVertices = new Array;
for (i = 0; i < noVertices; i++) {
a = shape[0][i];
ax = a[0];
ay = a[1];
az = a[2];
rx = rot[0][0] * ax + rot[0][1] * ay + rot[0][2] * az;
ry = rot[1][0] * ax + rot[1][1] * ay + rot[1][2] * az;
rz = rot[2][0] * ax + rot[2][1] * ay + rot[2][2] * az;
rotatedPoint = [rx, ry, rz];
rotatedVertices[i] = rotatedPoint
}
var projectedPoints = new Array;
for (i = 0; i < noVertices; i++) {
a = rotatedVertices[i];
projectedPoints[i] = projectPointOntoPlane(a, n, c);
}
var startPointIndex, endPointIndex, startPoint, endPoint;
var noFaceVertices;
//draw faces
ctx.fillStyle = faceFillStyle;
for (i = 0; i < noFaces; i++) {
noFaceVertices = shape[2][i].length;
startPointIndex = shape[2][i][0];
startPoint = projectedPoints[startPointIndex];
x = vectorDotProduct(xAxis, startPoint);
y = vectorDotProduct(yAxis, startPoint);
ctx.beginPath();
ctx.moveTo(xOrigin + x * scaling, yOrigin + y * scaling);
for (j = 1; j < noFaceVertices; j++) {
endPointIndex = shape[2][i][j];
endPoint = projectedPoints[endPointIndex];
x = vectorDotProduct(xAxis, endPoint);
y = vectorDotProduct(yAxis, endPoint);
ctx.lineTo(xOrigin + x * scaling, yOrigin + y * scaling);
}
ctx.fill();
}
// draw edges
ctx.strokeStyle = edgeStrokeStyle;
for (i = 0; i < noEdges; i++) {
startPointIndex = shape[1][i][0];
endPointIndex = shape[1][i][1];
startPoint = projectedPoints[startPointIndex];
endPoint = projectedPoints[endPointIndex];
x = vectorDotProduct(xAxis, startPoint);
y = vectorDotProduct(yAxis, startPoint);
ctx.beginPath();
ctx.moveTo(xOrigin + x * scaling, yOrigin + y * scaling);
x = vectorDotProduct(xAxis, endPoint);
y = vectorDotProduct(yAxis, endPoint);
ctx.lineTo(xOrigin + x * scaling, yOrigin + y * scaling);
ctx.stroke();
}
}
function regularTetrahedron() {
var vertices = [[1, 1, 1], [1, -1, -1], [-1, 1, -1], [-1, -1, 1]];
var edges = [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]];
var faces = [[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]];
var ans = [vertices, edges, faces];
return ans;
}
function regularCube() {
var vertices = [[1, 1, 1], [1, 1, -1], [1, -1, 1], [1, -1, -1],
[-1, 1, 1], [-1, 1, -1], [-1, -1, 1], [-1, -1, -1]];
var edges = [[0, 1], [0, 2], [0, 4], [1, 3], [1, 5],
[2, 3], [2, 6], [3, 7], [4, 5], [4, 6], [5, 7], [6, 7]];
var faces = [[0, 1, 3, 2], [0, 1, 5, 4], [1, 5, 7, 3],
[0, 2, 6, 4], [4, 5, 7, 6], [2, 3, 7, 6]];
var ans = [vertices, edges, faces];
return ans;
}
function regularOctahedron() {
var vertices = [[1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -1, 0],
[0, 0, 1], [0, 0, -1]];
var edges = [[0, 2], [2, 1], [1, 3], [3, 0],
[4, 0], [4, 2], [4, 1], [4, 3],
[5, 0], [5, 2], [5, 1], [5, 3]];
var faces = [[0, 2, 4], [0, 3, 4], [1, 2, 4], [1, 3, 4],
[0, 2, 5], [0, 3, 5], [1, 2, 5], [1, 3, 5]];
var ans = [vertices, edges, faces];
return ans;
}
function regularDodecahedron() {
var phi1 = (1 + Math.sqrt(5)) / 2;
var phi2 = 1/phi1;
var vertices = [[1, 1, 1], [1, 1, -1], [1, -1, 1], [1, -1, -1],
[-1, 1, 1], [-1, 1, -1], [-1, -1, 1], [-1, -1, -1],
[0, phi1, phi2], [0, phi1, -phi2], [0, -phi1, phi2], [0, -phi1, -phi2],
[phi2, 0, phi1], [-phi2, 0, phi1], [phi2, 0, -phi1], [-phi2, 0, -phi1],
[phi1, phi2, 0], [phi1, -phi2, 0], [-phi1, phi2, 0], [-phi1, -phi2, 0]];
var edges = [[0, 12], [0, 8], [0, 16], [1, 9], [1, 14], [1, 16],
[2, 10], [2, 12], [2, 17], [3, 11], [3, 14], [3, 17],
[4, 8], [4, 13], [4, 18], [5, 9], [5, 15], [5, 18],
[6, 10], [6, 13], [6, 19], [7, 11], [7, 15], [7, 19],
[8, 9], [10, 11], [12, 13], [14, 15], [16, 17], [18, 19]];
var faces = [[0, 12, 2, 17, 16], [0, 8, 4, 13, 12], [0, 8, 9, 1, 16],
[1, 14, 3, 17, 16], [1, 9, 5, 15, 14], [2, 10, 11, 3, 17],
[2, 12, 13, 6, 10], [3, 11, 7, 15, 14], [4, 13, 6, 19, 18],
[4, 8, 9, 5, 18], [5, 15, 7, 19, 18],[6,10,11,7,19]];
var ans = [vertices, edges, faces];
return ans;
}
function regularIcosahedron() {
var phi = (1 + Math.sqrt(5)) / 2;
var vertices = [[0, 1, phi], [0, 1, -phi], [0, -1, phi], [0, -1, -phi],
[1, phi, 0], [1, -phi, 0], [-1, phi, 0], [-1, -phi, 0],
[phi, 0, 1], [phi, 0, -1], [-phi, 0, 1], [-phi, 0, -1]];
var edges = [[0, 2], [0, 4], [0, 6], [0, 8], [0, 10],
[1, 3], [1, 4], [1, 6], [1, 9], [1, 11],
[2, 5], [2, 7], [2, 8], [2, 10],
[3, 5], [3, 7], [3, 9], [3, 11],
[4, 6], [4, 8], [4, 9], [5, 7], [5, 8], [5, 9],
[6, 10], [6, 11], [7, 10], [7, 11], [8, 9], [10, 11]];
var faces = [[0, 2, 8], [0, 2, 10], [0, 4, 6], [0, 4, 8], [0, 6, 10],
[1, 3, 9], [1, 3, 11], [1, 4, 6], [1, 4, 9], [1, 6, 11],
[2, 5, 7], [2, 5, 8], [2, 7, 10],
[3, 5, 7], [3, 5, 9], [3, 7, 11],
[4, 8, 9], [5, 8, 9], [6, 10, 11], [7, 10, 11]];
var ans = [vertices, edges, faces];
return ans;
}
function projectPointOntoPlane(a, n, c) {
// a is the point we want to project
// plane is defined by normal direction n (3-vector) and c = x.n
// where x are points on plane and "." is the dot product
var ans = [0, 0, 0];
var d;
d = a[0] * n[0] + a[1] * n[1] + a[2] * n[2] - c;
ans[0] = a[0] - d * n[0];
ans[1] = a[1] - d * n[1];
ans[2] = a[2] - d * n[2];
return ans;
}
function rotationMatrix(u, theta) {
//u is the axis of rotation (unit vector)
//theta is the amount of rotation around axis
//returned is a 3 x 3 matrix representing transformation
var ans = [[0, 0, 0], [0, 0, 0], [0, 0, 0]];
var c = Math.cos(theta);
var c1 = 1 - c;
var s = Math.sin(theta);
var ux = u[0];
var uy = u[1];
var uz = u[2];
var uxs = ux * s;
var uys = uy * s;
var uzs = uz * s;
var uxc1 = ux * c1;
var uxuyc1 = uy * uxc1;
var uxuzc1 = uz * uxc1;
var uyuzc1 = uy * uz * c1;
ans[0][0] = c + ux * uxc1;
ans[0][1] = uxuyc1 - uzs;
ans[0][2] = uxuzc1 + uys;
ans[1][0] = uxuyc1 + uzs;
ans[1][1] = c + uy * uy * c1;
ans[1][2] = uyuzc1 - uxs;
ans[2][0] = uxuzc1 - uys;
ans[2][1] = uyuzc1 + uxs;
ans[2][2] = c + uz * uz * c1;
return ans;
}
function unitiseVector(u){
//returns a vector in same direction
//but of unit length
var ux = u[0];
var uy = u[1];
var uz = u[2];
var s = ux * ux + uy * uy + uz * uz;
var s = Math.sqrt(s);
var ans = [ux / s, uy / s, uz / s];
return ans;
}
function vectorCrossProduct(u, v) {
var ux = u[0];
var uy = u[1];
var uz = u[2];
var vx = v[0];
var vy = v[1];
var vz = v[2];
var ans = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx];
return ans;
}
function vectorDotProduct(u, v) {
var ans = u[0] * v[0] + u[1] * v[1] + u[2] * v[2];
return ans;
}
</script>
</body>
</html>
|