Skip to content

Instantly share code, notes, and snippets.

@thinkbigthings
Last active March 20, 2018 11:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thinkbigthings/1c5a47575c55f2f7004da60a633edf38 to your computer and use it in GitHub Desktop.
Save thinkbigthings/1c5a47575c55f2f7004da60a633edf38 to your computer and use it in GitHub Desktop.
plot lots of points on a canvas using real-to-screen coordinate transformation
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="1000" height="500" style="border:1px solid #000000;">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
// experiment with big data sets (1M seems ok)
const data = Array(500000);
for(let i=0; i<data.length; i++) {
// data[i] = { x:i, y:Math.random()*50 - 25};
data[i] = { x:i*2*3.1416/data.length, y:Math.sin(i*2*3.1416/data.length)};
}
// Math.max overflows if array is too big, use reduce instead
const fastMin = (array) => array.reduce( (a,b) => (a < b) ? a : b );
const fastMax = (array) => array.reduce( (a,b) => (a > b) ? a : b );
let domain = data.map(p => p.x);
let range = data.map(p => p.y);
// real coordinates
let r={
xmin:fastMin(domain),
xmax:fastMax(domain),
ymin:fastMin(range),
ymax:fastMax(range)
};
// screen coordinates
let s={
ymin:0,
xmin:0,
xmax:1000,
ymax:500
};
// TODO unit test the equation, but I think this is right
// TODO Can save my ideas in an account in codepen
let xscale = (s.xmax-s.xmin)/(r.xmax-r.xmin);
let yscale = (s.ymax-s.ymin)/(r.ymax-r.ymin);
const toScreenX = (x) => ( x-r.xmin)*xscale;
const toScreenY = (y) => (-y-r.ymin)*yscale;
const realToScreen = function(p, callback) {
callback( toScreenX(p.x), toScreenY(p.y) );
}
const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
const plot = (x,y) => ctx.fillRect(x, y, 1, 1);
// terms: clipping, viewport, real/world coordinates, screen coordinates, grouping
// TODO mouse move/drag interaction
// TODO use a viewport for clipping
// TODO use grouping to reduce number of points to render
// TODO write as a react component
const start = new Date().getTime();
// reset entire canvas with bg color
ctx.fillStyle="#FFFFFF";
ctx.fillRect(0,0,1000,500);
// draw the points
ctx.fillStyle="#BBBBFF";
data.forEach(p => realToScreen(p, plot));
const finish = new Date().getTime();
console.log(finish - start);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment