<div class="canvas" name="PointCloud">
<canvas id="PointCloud" width="200" height="200"></canvas>
<script id="PointCloud.js">
(function(){
	var canvas = document.getElementById("PointCloud");
	var ctx = canvas.getContext("2d");
	ctx.fillStyle = 'rgb(192,0,0)';
	ctx.strokeStyle = 'rgb(0,0,0)';
	ctx.lineWidth = 0.5;

	var id = 0;
	canvas.tabIndex = 0;
	canvas.onmouseover = canvas.focus;
	canvas.onmouseout = canvas.blur;
	canvas.onmousemove = onMouseMove;
	canvas.onblur = function(){cancelAnimationFrame(id); id = 0;};
	canvas.onfocus = function(){if (!id) id = requestAnimationFrame(update, canvas);};
	function update() {id = requestAnimationFrame(update,canvas); draw();}

	var angle_x = 0;
	var angle_y = 0;
	function onMouseMove(event) {
		var x = event.offsetX;
		var y = event.offsetY;
		if (x>0 && x<canvas.width && y>0 && y<canvas.height) {
			angle_x = (x-canvas.width/2)/(canvas.width/2);
			angle_y = (y-canvas.height/2)/(canvas.height/2);
		}
	}

	var scalar = 1000;
	var model = {};
	fetch('bunny.obj').then(r => r.text()).then((data) => {
		loadmodel(data, model);
		buildmodel(model, scalar);
		draw();
	});

	var focal_length = 950;
	var distance = 1000;
	var screen_center = [canvas.width/2, canvas.height/2, 0];

	function draw() {
		ctx.clearRect(0, 0, canvas.width, canvas.height);

		var c = Math.cos(angle_x);
		var s = Math.sin(angle_x);
		var rx = [c,0,-s,0,1,0,s,0,c];
		var c = Math.cos(-angle_y);
		var s = Math.sin(-angle_y);
		var ry = [1,0,0,0,c,s,0,-s,c];

		for (let i=0; i<model.points.length; i++) {
			var p = mul(ry, mul(rx, model.points[i]));
			translate(p, [0,0,distance]);
			project(p, focal_length);
			translate(p, screen_center);
			ctx.fillRect(p[0],p[1],2,2);
		}
	}

	function translate(p, v) {
		p[0] += v[0];
		p[1] += v[1];
		p[2] += v[2];
	}

	function scale(p, v) {
		p[0] *= v[0];
		p[1] *= v[1];
		p[2] *= v[2];
	}

	function mul(m, p) {
		var q = new Array();
		q[0] = m[0] * p[0] + m[1] * p[1] + m[2] * p[2];
		q[1] = m[3] * p[0] + m[4] * p[1] + m[5] * p[2];
		q[2] = m[6] * p[0] + m[7] * p[1] + m[8] * p[2];
		return q;
	}

	function project(p, focal_length) {
		p[0] = p[0] * focal_length / p[2];
		p[1] = p[1] * focal_length / p[2];
		p[2] = focal_length;
	}

	function loadmodel(data, model) {
		model.points = new Array();
		model.faces  = new Array();

		var lines = data.split('\n');
		for (let line of lines) {
			if (line.length == 0 || line[0] == '#') continue;
			var s = line.match(/\S+/g);
			switch (s[0]) {
			case 'v':
				model.points.push([parseFloat(s[1]), parseFloat(s[2]), parseFloat(s[3])]);
				break;
			case 'f':
				model.faces.push([parseInt(s[1]) - 1, parseInt(s[2]) - 1, parseInt(s[3]) - 1]);
				break;
			}
		}
	}

	function buildmodel(model, scalar) {
		var max = [-Infinity, -Infinity, -Infinity];
		var min = [+Infinity, +Infinity, +Infinity];
		for (var i=0; i<model.points.length; i++) {
			max[0] = Math.max(max[0], model.points[i][0]);
			max[1] = Math.max(max[1], model.points[i][1]);
			max[2] = Math.max(max[2], model.points[i][2]);
			min[0] = Math.min(min[0], model.points[i][0]);
			min[1] = Math.min(min[1], model.points[i][1]);
			min[2] = Math.min(min[2], model.points[i][2]);
		}
		var center = [0,0,0];
		center[0] = -(max[0] + min[0]) / 2;
		center[1] = -(max[1] + min[1]) / 2;
		center[2] = -(max[2] + min[2]) / 2;
		for (var i=0; i<model.points.length; i++) {
			translate(model.points[i], center);
			scale(model.points[i], [-scalar,-scalar,-scalar]);
		}
	}
})();
</script>
</div>