Question: What in the world is this piece of code doing?
Also: Is the way 'w' is being used some sort of existing algorithm? I'm trying to figure out the intent of the function, or at least describe what sorts of numbers it produces.
Context: I'm looking at Martin O'Leary's "Fantasy Map Generation" code - full source here, which in short summary generates fantasy maps on the canvas. There is some insightful explanations of how the higher level process works in a blog post, but this is too low level to get any coverage there. There is a particular function called 'rnorm' that gets used in a couple of places, and I'm lost at how it works. I've included it below, followed by a couple of instances where it comes up for some context. Any help on what this thing is doing would be great!
var rnorm = (function() {
var z2 = null;
function rnorm() {
if (z2 != null) {
var tmp = z2;
z2 = null;
return tmp;
}
var x1 = 0;
var x2 = 0;
var w = 2.0;
while (w >= 1) {
x1 = runif(-1, 1);
x2 = runif(-1, 1);
w = x1 * x1 + x2 * x2;
}
w = Math.sqrt(-2 * Math.log(w) / w);
z2 = x2 * w;
return x1 * w;
}
return rnorm;
})();
runif(), which is called in the code above, is a short function that generates a random number between two given values
function runif(lo, hi) {
return lo + Math.random() * (hi - lo);
}
This code is used to produce random vectors (actually the only place it's used during the generation process) -
function randomVector(scale) {
return [scale * rnorm(), scale * rnorm()];
}
But I think it's doing more than that because the following, when provided a direction of 'randomVector(4),' produces a gradual slope over the entire mesh heightmap: EDIT: no, it actually is having no effect on the gradual slope. That comes from some sneakyness using the fact that one side of the map is 0,0, and the other side of the map is width,height, which creates numbers that gradually increase.
function slope(mesh, direction) {
return mesh.map(function (x) {
return x[0] * direction[0] + x[1] * direction[1];
});
}
Let me know if there's anything else I should be providing. This is my first question here, so I may be a little soft on conventions.
I think it's horrible code. It appears to create a pair of values, z1 and z2, but instead of putting them in a tuple and returning that it returns z1 and on every second call the corresponding z2 value. I have no idea why they'd do such a thing, my only guess would be to avoid allocation of objects and make usage syntactically more convenient.
It should be simplified to
function rnorm() {
var x1 = 0;
var x2 = 0;
var w = 2.0;
while (w >= 1) {
x1 = runif(-1, 1);
x2 = runif(-1, 1);
w = x1 * x1 + x2 * x2;
}
w = Math.sqrt(-2 * Math.log(w) / w);
return [x1 * w, x2 * w];
}
function randomVector(scale) {
var [z1, z2] = rnorm();
return [scale * z1, scale * z2];
}
A modern compiler should be able to avoid array allocation for the returned literal and subsequent destructuring. If it's not, you can do it manually by inlining rnorm in randomVector, especially if this is the only place where it's called anyway.
Related
Trying to write a simple web app to solve the following common calculus problem in JavaScript.
Suppose you wanted to make an open-topped box out of a flat piece of cardboard that is L long by W wide by cutting the same size
square (h × h) out of each corner and then folding the flaps to form the box,
as illustrated below:
You want to find out how big to make the cut-out squares in order to maximize the volume of the box.
Ideally I want to avoid using any calculus library to solve this.
My initial naive solution:
// V = l * w * h
function getBoxVolume(l, w, h) {
return (l - 2*h)*(w - 2*h)*h;
}
function findMaxVol(l, w) {
const STEP_SIZE = 0.0001;
let ideal_h = 0;
let max_vol = 0;
for (h = 0; h <= Math.min(l, w) / 2; h = h + STEP_SIZE) {
const newVol = getBoxVolume(l, w, h);
if (max_vol <= newVol) {
ideal_h = h;
max_vol = newVol;
} else {
break;
}
}
return {
ideal_h,
max_vol
}
}
const WIDTH_1 = 20;
const WIDTH_2 = 30;
console.log(findMaxVol(WIDTH_1, WIDTH_2))
// {
// ideal_h: 3.9237000000038558,
// max_vol: 1056.3058953402121
// }
The problem with this naive solution is that it only gives an estimate because you have to provide STEP_SIZE and it heavily limits the size of the problem this can solve.
You have an objective function: getBoxVolume(). Your goal is to maximize the value of this function.
Currently, you're maximizing it using something equivalent to sampling: you're checking every STEP_SIZE, to see whether you get a better result. You've identified the main problem: there's no guarantee the edge of the STEP_SIZE interval falls anywhere near the max value.
Observe something about your objective function: it's convex. I.e., it starts by going up (when h = 0, volume is zero, then it grows as h does), it reaches a maximum, then it goes down, eventually reaching zero (when h = min(l,w)/2).
This means that there's guaranteed to be one maximum value, and you just need to find it. This makes this problem a great case for binary search, because given the nature of the function, you can sample two points on the function and know which direction the maximum lies relative to those two points. You can use this, with three points at a time (left, right, middle), to figure out whether the max is between left and middle, or middle and right. Once these values get close enough together (they're within some fixed amount e of each other), you can return the value of the function there. You can even prove that the value you return is within some value e' of the maximum possible value.
Here's pseudocode:
max(double lowerEnd, upperEnd) {
double midPoint = (upperEnd + lowerEnd) / 2
double midValue = getBoxVolume(l, w, midpoint)
double slope = (getBoxVolume(l, w, midpoint + epsilon) - midValue) / epsilon
if (Math.abs(slope) < epsilon2) { // or, if you choose, if (upperEnd - lowerEnd < epsilon3)
return midpoint
}
if (slope < 0) { // we're on the downslope
return max(lowerEnd, midPoint)
}
else { // we're on the up-slope
return max(midpoint, upperEnd)
}
}
After realising that the derivative of the volume function is a second degree polynomial you can apply a quadratic formula to solve for x.
Using calculus, the vertex point, being a maximum or minimum of the function, can be obtained by finding the roots of the derivative
// V = l * w * h
function getBoxVolume(l, w, h) {
return (l - 2*h)*(w - 2*h)*h;
}
// ax^2 + bx + c = 0
function solveQuad(a, b, c) {
var x1 = (-1 * b + Math.sqrt(Math.pow(b, 2) - (4 * a * c))) / (2 * a);
var x2 = (-1 * b - Math.sqrt(Math.pow(b, 2) - (4 * a * c))) / (2 * a);
return { x1, x2 };
}
function findMaxVol(l, w) {
// V'(h) = 12h^2-4(l+w)h+l*w - second degree polynomial
// solve to get the critical numbers
const result = solveQuad(12, -4*(l + w), l*w)
const vol1 = getBoxVolume(l, w, result.x1);
const vol2 = getBoxVolume(l, w, result.x2);
let ideal_h = 0;
let max_vol = 0;
// check for max
if (vol1 > vol2) {
ideal_h = result.x1;
max_vol = vol1;
} else {
ideal_h = result.x2;
max_vol = vol2;
}
return {
ideal_h,
max_vol
}
}
const WIDTH_1 = 20;
const WIDTH_2 = 30;
console.log(findMaxVol(WIDTH_1, WIDTH_2))
// {
// ideal_h: 3.9237478148923493,
// max_vol: 1056.30589546119
// }
Im creating an object that randomly moves in a natural way using noise like this (works as intended):
The objects encounter a collision and their trajectory is manipulated, the movement path now changes to straight line (words as intended)
thisRabbit.x = _world.width * (noise(thisRabbit.t));
thisRabbit.y = _world.height * (noise(thisRabbit.t+5));
thisRabbit.t += 0.001;
The problem is after this movement , i want the object to start moving in a random direction again as it was initially. If i use the same function, the object jumps to the last location before the trajectory was modified.
let vx = this.acquiredFood[0] - this.x;
let vy = this.acquiredFood[1] - this.y;
let f = (this.genes.speed + 10) / Math.sqrt(vx*vx+vy*vy);
vx = vx * f;
vy = vy * f;
let newX = this.x + vx;
let newY = this.y + vy;
So how do i get the object to move as before, given a starting position
edit: snippet here: https://editor.p5js.org/vince.chinner/sketches/HPFKR8eIw
Your problem is that you used a factor from 0 to 1 generated with noise and an incremented seed to generate the position by multiplying directly the world dimentions. When reaching food, you cannot increment the seed as to be in the exact position where the movement to get your food led you (I found no inverse function for noise to get the seed from the return value).
What you need to do instead is use the noise to increment or decrement the coordinates, so that no matter where the seed is, you don't loose your current position.
Here are the different corrections I applied to the code, as there were also syntax errors, I can't really paste the whole stuff here for copyright reasons (you didn't share the whole code here and the sketch belongs to you)
MAIN CORRECTION:
used a var found because returning from the forEach callback doesn't make you leave the findFood function, but the callback one. And the forEach loop doesn't stop. Using this var prevents the further forEach tests to be made and allows you to return from findFood so that no further move is made after seeing food.
noise is now applied to a value of 4 and I subtract 2, so that x and y now change with a range of -2 to 2 each. Of course, with this method, you need to check against world dimentions or else the rabbit could leave the world. The seed increment has been changed too or else it would vary too slowly (adapt values as you wish)
findFood(){
var thisRabbit = this, found = false;
_world.food.forEach(f => {
if(!found){
let d = int(dist(f[0], f[1], thisRabbit.x, thisRabbit.y));
if(d < (thisRabbit.genes.vision / 2)+3){
thisRabbit.state = "foundFood";
this.acquiredFood = f;
found = true;
}
}
});
if(found){ return; }
thisRabbit.x += (noise(thisRabbit.t) * 4) - 2;
if(thisRabbit.x < 0){ thisRabbit.x = 0; }
if(thisRabbit.x > _world.width){ thisRabbit.x = _world.width; }
thisRabbit.y += (noise(thisRabbit.t + 5) * 4) - 2;
if(thisRabbit.y < 0){ thisRabbit.y = 0; }
if(thisRabbit.y > _world.height){ thisRabbit.y = _world.height; }
thisRabbit.t += 0.01;
}
SYNTAX ERRORS:
lines 23 / 24: assignment should be with a value (null or false)
this.genes = null;
this.acquiredFood = null;
lines 129 to 133: end you instructions with a ; instead of a ,
this.width = w;
this.height = h;
this.foodDensity = foodDensity;
this.food = [];
this.rabits = [];
line 156 to 160: there should be no space between rabbit and .t. Additionnally, because the coordinates are not directly linked to t, I would prefer to use random for starting position:
let x = this.width * random();
let y = this.height * random();
let _rabbit = new rabbit(x, y);
_rabbit.genes = genes;
_rabbit.t = t;
All my searching comes up with more general arc/sin/cos usage or shooting to the mouse position.
I am looking to aim and fire a projectile with the keyboard and have done a lot of it from scratch, as a noob in a web class doing a project, but I am stuck on this. My current math got me to this mess in firing the shot in the direction the line is currently pointing... (code names cleaned for readability):
this.x = x + len * Math.cos(angle);
this.y = y + len * Math.sin(angle);
this.xmov = -((x + len * Math.cos(angle)) - x) / ((y + len * Math.sin(angle)) - y);
this.ymov = ((y + len * Math.sin(angle)) - y) / ((x + len * Math.cos(angle)) - x);
if (Math.abs(this.xmov) > Math.abs(this.ymov)) {
this.xmove = (this.xmov * Math.abs(this.ymov));
} else {
this.xmove = this.xmov;
}
if (Math.abs(this.ymov) > Math.abs(this.xmov)) {
this.ymove = (this.xmov * this.ymov);
} else {
this.ymove = this.ymov;
}
(And here is the full thing http://jsbin.com/ximatoq/edit. A and D to turn, S to fire (on release). Can also hold S while turning.)
... but, you'll see that it only works for 3/8's of it. What is the math to make this fire from a complete circle?
Use this as shoot function:
this.shoot = function() {
if (this.fire > 0) {
this.x = P1gun.x2;
this.y = P1gun.y2;
this.xmove = (P1gun.x2 - P1gun.x)/100;
this.ymove = (P1gun.y2 - P1gun.y)/100;
this.fire = 0;
this.firetravel = 1;
}
}
The /100 can be removed, but you have to reduce the projectile speed.
If you want to shoot gun2 change the P1gun to P2gun.
Normalising a vector.
To control the speed of something using a vector, first make the length of the vector 1 unit long (one pixel). This is commonly called normalising the vector, and sometimes it's called the unit vector. Then you can multiply that vector by any number to get the desired speed.
To normalise a vector first calculate its length, then divide it by that value.
function normalizeVector(v){
var len = Math.sqrt(v.x * v.x + v.y * v.y);
v.x /= len;
v.y /= len;
return v;
}
Trig
When you use trig to create a vector it is also a unit vector and does not need to be normalised.
function directioToUnitVector(angle){ // angle in radians
return {
x : cos(angle),
y : sin(angle)
}
Why normalise
Many many reasons, you build almost everything from unit vectors.
One example, if you have two points and want to move from one to the next at a speed of 10 pixels per second with a frame rate of 60frame per second.
var p1 = {};
var p2 = {};
p1.x = ? // the two points
p1.y = ?
p2.x = ?
p2.y = ?
// create a vector from p1 to p2
var v = {}
v.x = p2.x -p1.x;
v.y = p2.y -p1.y;
// Normalize the vector
normalizeVector(v);
var frameRate = 1/60; // 60 frames per second
var speed = 10; // ten pixels per second
function update(){
// scale vec to the speed you want. keeping the vec as a unit vec mean
// you can also change the speed, or use the time for even more precise
// speed control.
p1.x += v.x * (speed * frameRate);
p1.y += v.y * (speed * frameRate);
// draw the moving object at p1
requestAnimationFrame(update)
}
NOTE when normalizing you may get a vector that has no length. If your code is likely to create such a vector you need to check for the zero length and take appropriate action. Javascript does not throw an error when you divide by zero, but will return Infinity, with very strange results to your animations.
I've been trying to implement collision detection between circles and polygons based on Randy Gaul's C++ Impulse Engine, following the code pretty closely, but the algorithm never returns true.
Here's the JSFiddle. (the bodies are rendered using the HTML5 Canvas API for convenience)
A snippet of the code (just collision detection):
const circPoly = (a, b) => {
let data = {},
center = a.pos;
data.contacts = [];
center = b.mat.clone().trans().mult(center.clone().sub(b.pos));
let sep = -Number.MAX_VALUE,
faceNorm = 0;
for (let i = 0; i < b.verts2.length; ++i) {
let sep2 = b.norms[i].dot(center.clone().sub(b.verts2[i]));
if (sep2 > a.radius) return data;
if (sep2 > sep) { sep = sep2; faceNorm = i; }
}
let v1 = b.verts2[faceNorm],
v2 = b.verts2[faceNorm + 1 < b.verts2.length ? faceNorm + 1 : 0];
if (sep < 0.0001) {
data.depth = a.radius;
data.norm = b.mat.clone().mult(b.norms[faceNorm]).neg();
data.contacts[0] = data.norm.clone().vmult(a.pos.clone().sadd(a.radius));
return data;
}
let dot1 = center.clone().sub(v1).dot(v2.clone().sub(v1)),
dot2 = center.clone().sub(v2).dot(v1.clone().sub(v2));
data.depth = a.radius - sep;
if (dot1 <= 0) {
if (center.dist2(v1) > a.radius * a.radius) return data;
let norm = v1.clone().sub(center);
norm = b.mat.clone().mult(norm);
norm.norm();
data.norm = norm;
v1 = b.mat.clone().mult(v1.clone().add(b.pos));
data.contacts[0] = v1;
} else if (dot2 <= 0) {
if (center.dist2(v2) > a.radius * a.radius) return data;
let norm = v2.clone().sub(center);
norm = b.mat.clone().mult(norm);
norm.norm();
data.norm = norm;
v2 = b.mat.clone().mult(v2.clone().add(b.pos));
data.contacts[0] = v2;
} else {
let norm = b.norms[faceNorm];
if (center.clone().sub(v1).dot(norm) > a.radius) return data;
norm = b.mat.clone().mult(norm);
data.norm = norm.clone().neg();
data.contacts[0] = data.norm.clone().vmult(a.pos.clone().sadd(a.radius));
}
return data;
};
Note that b.verts2 refers to the polygon's vertices in real world coordinates.
I know for a fact that there are no problems with the Vector class but as I don't exactly have very much experience with transformation matrices, that class could be the root of these errors, although the code for it is pretty much entirely derived from the Impulse Engine as well, so it should work. As mentioned before, the algorithm always returns false, even when a collision really has occurred. What am I doing wrong here? I tried taking out the early returns, but that just returns weird results like contact points with negative coordinates which obviously is not quite correct.
EDIT: Modified my vector class's perpendicular function to work the same way as the Impulse Engine's (both ways are right, but I think one is clockwise and the other one counterclockwise -- I also modified my vertices to reflect the counterclockwise-ness). Unfortunately, it still fails the test.
https://jsfiddle.net/khanfused/tv359kgL/4/
Well the are many problems and I really dont understand what you are trying to do as it seem overly complex. Eg why does matrix have trans??? and why are you using the Y up screen as the coordinate system for the transform??? (rhetorical)
In the first loop.
The first is that you are testing the distance of the normal vectors
of each vert, should be testing the vert position.
Also you are finding the distance using the vec.dot function that
returns the square of the distance. But you test for the radius, you
should be testing for if(sep2 < radius * radius)
And you have the comparison the wrong way around you should be
testing if less than radius squared (not greater than)
Then when you do detect a vert within the radius you return the data
object but forget to put the vert that was found inside the circle on
the data.contacts array.
I am not sure what the intention of keeping the index of the most
distant vect is but then the rest of the function make zero sense to
me???? :( and I have tried to understand it.
All you need to do is
A check if any verts on the poly are closer than radius, if so then you have a intercept (or is completely inside)
Then you need to check the distance of each line segment
Can be done for each line segment with the following if you dont need the intercepts (or below that if you need intercepts) only use one or the other.
// circle is a point {x:?,y:?}
// radius = is the you know what
// p1,p2 are the start and end points of a line
checkLineCircle = function(circle,radius,p1,p2){
var v1 = {};
var v2 = {};
var v3 = {};
var u;
// get dist to end of line
v2.x = circle.x - p1.x;
v2.y = circle.y - p1.y;
// check if end points are inside the circle
if( Math.min(
Math.hypot(p2.x - circle.x, p2.y - circle.y),
Math.hypot(v2.x, v2.y)
) <= radius){
return true;
}
// get the line as a vector
v1.x = p2.x - p1.x;
v1.y = p2.y - p1.y;
// get the unit distance of the closest point on the line
u = (v2.x * v1.x + v2.y * v1.y)/(v1.y * v1.y + v1.x * v1.x);
// is this on the line segment
if(u >= 0 && u <= 1){
v3.x = v1.x * u; // get the point on the line segment
v3.y = v1.y * u;
// get the distance to that point and return true or false depending on the
// it being inside the circle
return (Math.hypot(v3.y - v2.y, v3.x - v2.x) <= radius);
}
return false; // no intercept
}
Do that for each line.To save time transform the circle center to the polygon local, rather than transform each point on the poly.
If you need the points of intercept then use the following function
// p1,p2 are the start and end points of a line
// returns an array empty if no points found or one or two points depending on the number of intercepts found
// If two points found the first point in the array is the point closest to the line start (p1)
function circleLineIntercept(circle,radius,p1,p2){
var v1 = {};
var v2 = {};
var ret = [];
var u1,u2,b,c,d;
// line as vector
v1.x = p2.x - p1.x;
v1.y = p2.y - p1.y;
// vector to circle center
v2.x = p1.x - circle.x;
v2.y = p1.y - circle.y;
// dot of line and circle
b = (v1.x * v2.x + v1.y * v2.y) * -2;
// length of line squared * 2
c = 2 * (v1.x * v1.x + v1.y * v1.y);
// some math to solve the two triangles made by the intercept points, the circle center and the perpendicular line to the line.
d = Math.sqrt(b * b - 2 * c * (v2.x * v2.x + v2.y * v2.y - radius * radius));
// will give a NaN if no solution
if(isNaN(d)){ // no intercept
return ret;
}
// get the unit distance of each intercept to the line
u1 = (b - d) / c;
u2 = (b + d) / c;
// check the intercept is on the line segment
if(u1 <= 1 && u1 >= 0){
ret.push({x:line.p1.x + v1.x * u1, y : line.p1.y + v1.y * u1 });
}
// check the intercept is on the line segment
if(u2 <= 1 && u2 >= 0){
ret.push({x:line.p1.x + v1.x * u2, y : line.p1.y + v1.y * u2});
}
return ret;
}
I will leave it up to you to do the polygon iteration.
I have a 3D matrix of size (X, Y, Z) which is stored in a data structure as Z matrices, each X x Y in size. I would like to re-slice these matrices to obtain X slices, each Y x Z in size. In other words, I want to reslice a 3D matrix stored as XY slices in the YZ plane. The use case is to reslice axial CT images into sagittal images. I am working inside a browser environment.
Here's an example of what I am trying to achieve:
I have implemented the naive (iterative) solution in Python, which takes O(Y * Z) per slice. I haven't even bothered writing out the corresponding JavaScript implementation, because this approach is too slow by several orders of magnitude.
import glob
import numpy as np
import matplotlib.pyplot as plt
from scipy.misc import imread
height, width, depth = 512, 512, 100
volume = np.zeros((height, width, depth))
s = 0
for filename in glob.iglob('./*.jpg'):
volume[:,:,s] = imread(filename)[...,0]/255.0
s += 1
reslice = np.zeros((depth, height, width))
for s in xrange(0, width):
current = np.zeros((depth, height))
for i in xrange(0, height):
for j in xrange(0, depth):
current[j,i] = volume[i,s,j]
reslice[:,:,s] = current
This algorithm seems to be amenable to parallelization. For example, in CUDA, one could load the 3D data into global memory, create one thread per pixel, then iterate for every slice in the new direction, and on each iteration ask the right pixels to fire in order to fill out the current slice. This would be a trivial kernel to write, and would be approximately O(1) per slice. However, I don't have access to CUDA in the browser.
Mapping from CUDA to WebCL is relatively straightforward, but WebCL is out of question given inexistent vendor support ATM. Therefore, I'm thinking WebGL is the ideal solution.
I'm not too sure how this would be done in the "WebGL" paradigm, but I'm sure it can be done, and I suspect it is fairly trivial as well. I can't seem to find where to start, however, and resources on doing general-purpose computations with OpenGL are extremely scarce. How would I go about using OpenGL to speed up reslicing of a 3D matrix inside the browser?
You don't have necessarily to use webGL to be fast enough.
If you use a 3D array, JavaScript might be too slow but by using a flat array the time to reslice is in fact similar to the time it takes to create the array!
Another trick is to use a typed array to reduce memory usage and improve performances (Uint8Array).
I created a small class to handle such a flat array and to slice it.
I think the most relevant thing you want is in fact to get a view, either on (x, y) axes or (y, z) axes.
Since Array creation is very slow, you want to build the view on place within a fixed buffer. And since you want also a sliced view, you have to create a buffer and method also for the sliced view.
It's fast: creating a view for your 512X512x100 example take less than 5 ms!
(So in fact, the putImageData you'll have to do afterward will quite take more time! )
Fiddle is here: http://jsfiddle.net/n38mwh95/1/
Here's the class handling the data, you'll have to change the constructor so it accepts the real raw data:
function Array3D(xSize, ySize, zSize) {
this.xSize = xSize;
this.ySize = ySize;
this.zSize = zSize;
var xyMultiplier = xSize * ySize;
this.array = new Uint8Array(xSize * ySize * zSize);
this.view = new Uint8Array(xSize * ySize);
this.slicedView = new Uint8Array(ySize * zSize);
this.valueAt = function (x, y, z) {
return this.array[x + xSize * (y + z * ySize)];
};
this.setValueAt = function (x, y, z, val) {
return this.array[x + xSize * (y + z * ySize)] = val;
};
this.buildView = function (z) {
var src = this.array;
var view = this.view;
for (var x = 0; x < xSize; x++) {
for (var y = 0; y < ySize; y++) {
view[x + xSize * y] = src[x + xSize * (y + z * ySize)];
}
}
return view;
};
this.buildSlicedView = function (x) {
var src = this.array;
var sView = this.slicedView;
for (var y = 0; y < ySize; y++) {
for (var z = 0; z < zSize; z++) {
sView[y + ySize * z] = src[x + xSize * (y + z * ySize)];
}
}
return sView;
};
}
In use:
var xSize = 512;
var ySize = 512;
var zSize = 100;
var t1, t2;
t1 = performance.now();
var testArray = new Array3D(xSize, ySize, zSize);
t2 = performance.now();
console.log('created in :' + (t2 - t1));
t1 = performance.now();
var resliced = testArray.buildView(10);
t2 = performance.now();
console.log('building view in :' + (t2 - t1));
var x = 80;
t1 = performance.now();
var resliced = testArray.buildSlicedView(x);
t2 = performance.now();
console.log('building sliced view in :' + (t2 - t1));
Results:
created in :33.92199998779688 (index):73
building view in :2.7559999871300533 (index):79
building sliced view in :5.726000003051013
At the end of the code I also added some code to render the view.
Don't forget to cache the canvas imageData: create it only once then re-use it for best performance.
You could easily have a real-time rendering in fact.