class Renderer {
constructor(options) {
const { numPoints, numLines } = options;
this.camera = this.createCamera(options);
this.scene = this.createScene(options);
this.renderer = this.createRenderer(options);
const positions = new THREE.BufferAttribute(new Float32Array(numPoints * 3), 3);
const indices = new THREE.BufferAttribute(new Uint16Array(numLines * 2), 1);
this.points = this.createPointsMesh(positions, options);
this.lines = this.createLinesMesh(positions, indices, options);
this.scene.add(this.lines);
this.scene.add(this.points);
}
autoZoom(pad = 0) {
this.__zoomOrthographic(this.points, this.camera, this.renderer, pad);
}
render() {
this.renderer.render(this.scene, this.camera);
}
createCamera(options) {
const {
cameraFar,
cameraFov,
cameraNear,
cameraX,
cameraY,
cameraZ,
height,
width
} = options;
//const camera = new THREE.PerspectiveCamera(cameraFov, width / height, cameraNear, cameraFar);
const wh = width / 2, hh = height / 2;
const camera = new THREE.OrthographicCamera( -wh, wh, hh, -hh, 1, cameraFar);
camera.position.set(cameraX, cameraY, cameraZ);
return camera;
}
/**
*/
createScene(options) {
const {backgroundColor} = options;
const scene = new THREE.Scene();
scene.background = new THREE.Color(backgroundColor);
return scene;
}
/**
*/
createRenderer(options) {
const {height, width} = options;
const renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(width, height);
renderer.setPixelRatio(window.devicePixelRatio);
return renderer;
}
/**
*/
createPointTexture(size) {
const ctx = DOM.context2d(size, size, 1);
ctx.fillStyle = 'rgba(255,255,255,0)';
ctx.fillRect(0, 0, size, size);
const sh = size / 2;
ctx.arc(sh, sh, sh, 0, Math.PI * 2);
ctx.fillStyle = '#fff';
ctx.fill();
const texture = new THREE.CanvasTexture(ctx.canvas);
//texture.premultipliedAlpha = true;
return texture;
}
/**
*/
createPointsMesh(positions, options) {
const { pointColor, pointSize, textureSize } = options;
const geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', positions);
const material = new THREE.PointsMaterial({
size: pointSize,
sizeAttenuation: true,
color: pointColor,
map: this.createPointTexture(textureSize),
alphaTest: .5,
transparent: true,
depthTest: true,
depthWrite: true
});
const mesh = new THREE.Points(geometry, material);
return mesh;
}
/**
*/
createLinesMesh(positions, indices, options) {
const { lineColor } = options;
const geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', positions);
geometry.setIndex(indices);
const material = new THREE.LineBasicMaterial({color: lineColor});
const mesh = new THREE.LineSegments(geometry, material);
return mesh;
}
/**
*/
setLinks(nodes, pairs) {
const map = {};
nodes.forEach((node, i) => map[node.id] = i);
const indices = this.lines.geometry.getIndex();
for(let i = 0; i < pairs.length; i++) {
indices.array[i] = map[pairs[i]];
}
indices.needsUpdate = true;
this.lines.geometry.setDrawRange(0, pairs.length);
}
/**
*/
setPositions(coordinates) {
const positions = this.points.geometry.attributes.position;
for(let i = 0; i < coordinates.length; i++) {
positions.array[i] = coordinates[i];
}
this.points.geometry.setDrawRange(0, coordinates.length / 3);
positions.needsUpdate = true;
}
/**
*/
__zoomOrthographic(target, camera, renderer, pad = 0) {
target.geometry.computeBoundingBox();
const box = target.geometry.boundingBox;
let width = box.max.x - box.min.x;
let height = box.max.y - box.min.y;
if(!width || !height) {
return;
}
const cx = box.min.x + width / 2;
const cy = box.min.y + height / 2;
const size = renderer.getSize();
const ratio = size.width / size.height;
const boxRatio = width / height;
if(boxRatio >= ratio) {
height = width / ratio;
}
else {
width = height * ratio;
}
const wh = width / 2;
const hh = height / 2;
camera.left = cx - wh - pad;
camera.right = cx + wh + pad;
camera.bottom = cy - hh - pad;
camera.top = cy + hh + pad;
camera.updateProjectionMatrix();
}
}