こんにちは。
大量の点の 2 次元プロットについて、regl (WebGL ライブラリ)を利用することによる高速性を試してみました(reglを利用する目的はWebGLを簡素に記述して利用したいためです)。mousedown, mouseup, wheel, resize イベントの処理については自前で行いました。
1000万点(ten millions)1 をプロットしてもスクロール・ズーム時の描画レスポンスについては良いようです。
scatter.html
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<style>
html,body{
height: 100%;
margin: 0;
}
.canvas{
height: 100%;
padding: 0px;
}
</style>
</head>
<body>
<div class="canvas"></div>
</body>
<script language="javascript" src="https://npmcdn.com/regl/dist/regl.min.js"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script language="javascript">
const pointSize = 8;
const nPoints = 100000;
const timerDelay = 100;
const deltaY = 100;
const rng = d3.randomNormal(0, 0.3);
const points = d3.range(nPoints).map(() =>
({ x: rng(),
y: rng(),
color: [Math.random(), Math.random(), 0]})
);
let scale = 1, offset = [0, 0];
let timer, dragStart;
const mouseDown = e => {
dragStart = {x: e.pageX, y: e.pageY};
}
const mouseUp = e => {
offset[0] += (e.pageX - dragStart.x) * 2 / window.innerWidth;
offset[1] += (e.pageY - dragStart.y) * 2 / window.innerHeight;
draw();
}
const wheelScroll = e => {
const magnification = Math.exp(-e.deltaY / deltaY);
scale *= magnification;
offset[0] *= magnification;
offset[1] *= magnification;
draw();
}
const resize = e => {
if (timer !== false) {
clearTimeout(timer);
}
timer = setTimeout(function () {
regl.poll();
draw();
}, timerDelay);
};
document.addEventListener("mousedown", mouseDown, false);
document.addEventListener("mouseup", mouseUp, false);
document.addEventListener("wheel", wheelScroll);
window.addEventListener('resize', resize);
const frag = `
precision highp float;
varying vec3 fragColor;
void main() {
gl_FragColor = vec4(fragColor, 1);
}`;
const vert = `
varying vec3 fragColor;
attribute vec2 pos;
attribute vec3 color;
uniform float pointSize;
uniform float scale;
uniform vec2 offset;
vec2 normalizeCoords(vec2 pos) {
float x = pos[0] * scale + offset[0];
float y = pos[1] * scale + offset[1];
return vec2(x, -y);
}
void main() {
fragColor = color;
gl_PointSize = pointSize;
gl_Position = vec4(normalizeCoords(pos), 0, 1);
}`;
const draw = () => {
drawPoints({scale: scale, offset: offset});
}
const regl = createREGL({
container: document.querySelector('.canvas'),
});
const drawPoints = regl({
frag: frag,
vert: vert,
primitive: 'points',
count: points.length,
attributes: {
pos: points.map(d => [d.x, d.y]),
color: points.map(d => d.color)
},
uniforms: {
pointSize: pointSize,
scale: regl.prop('scale'),
offset: regl.prop('offset'),
}
});
draw();
</script>
</html>
-
ただし 1億点をプロットしようとすると、配列管理の上限を超えるようで、異常発生しました。 ↩