dotCloudMesh = {
const dotCloudTriangles = 2e5,
curveTriangles = 1e5;
const geometry = new THREE.BufferGeometry();
const positions = [];
const normals = [];
const colors = [];
const color = new THREE.Color();
const xEnlarge = 2;
const pA = new THREE.Vector3();
const pB = new THREE.Vector3();
const pC = new THREE.Vector3();
const cb = new THREE.Vector3();
const ab = new THREE.Vector3();
let rndX = d3.randomUniform(d3.max(data, (d) => d.x)),
rndR = d3.randomUniform(-1, 1),
rndTheta = d3.randomUniform(2 * Math.PI),
rndDotSize = d3.randomUniform(-0.005, 0.005),
x,
y,
z,
r,
d,
d1,
d2,
theta;
function addDot(x, y, z, alpha = undefined) {
const ax = x + rndDotSize();
const ay = y + rndDotSize();
const az = z + rndDotSize();
const bx = x + rndDotSize();
const by = y + rndDotSize();
const bz = z + rndDotSize();
const cx = x + rndDotSize();
const cy = y + rndDotSize();
const cz = z + rndDotSize();
positions.push(ax, ay, az);
positions.push(bx, by, bz);
positions.push(cx, cy, cz);
// flat face normals
pA.set(ax, ay, az);
pB.set(bx, by, bz);
pC.set(cx, cy, cz);
cb.subVectors(pC, pB);
ab.subVectors(pA, pB);
cb.cross(ab);
cb.normalize();
const nx = cb.x;
const ny = cb.y;
const nz = cb.z;
normals.push(nx, ny, nz);
normals.push(nx, ny, nz);
normals.push(nx, ny, nz);
// colors
const vx = x + 0.5;
const vy = y + 0.5;
const vz = z + 0.5;
color.setRGB(vx, vy, vz);
alpha = alpha ? alpha : Math.random();
colors.push(color.r, color.g, color.b, alpha);
colors.push(color.r, color.g, color.b, alpha);
colors.push(color.r, color.g, color.b, alpha);
}
for (let i = 0; i < dotCloudTriangles; i++) {
// positions
x = rndX();
d = data.find((d) => d.x > x);
r = d ? d.r : 0;
r *= rndR();
theta = rndTheta();
y = Math.cos(theta) * r;
z = Math.sin(theta) * r;
x -= 0.5;
x *= xEnlarge;
addDot(x, y, z);
}
for (let i = 0; i < curveTriangles; ++i) {
x = rndX();
d1 = data.findLast((d) => d.x < x);
d2 = data.find((d) => d.x > x);
if ((d1 === undefined) & (d2 === undefined)) {
y = 0;
z = 0;
} else if (d1 === undefined) {
y = d2.y;
z = d2.h;
} else if (d2 === undefined) {
y = d1.y;
z = d1.h;
} else {
y = d3.scaleLinear().domain([d1.x, d2.x]).range([d1.y, d2.y])(x);
z = d3.scaleLinear().domain([d1.x, d2.x]).range([d1.h, d2.h])(x);
}
x -= 0.5;
x *= xEnlarge;
addDot(x, y, z, 1);
}
function disposeArray() {
this.array = null;
}
geometry.setAttribute(
"position",
new THREE.Float32BufferAttribute(positions, 3).onUpload(disposeArray)
);
geometry.setAttribute(
"normal",
new THREE.Float32BufferAttribute(normals, 3).onUpload(disposeArray)
);
geometry.setAttribute(
"color",
new THREE.Float32BufferAttribute(colors, 4).onUpload(disposeArray)
);
geometry.computeBoundingSphere();
const material = new THREE.MeshPhongMaterial({
color: 0xd5d5d5,
specular: 0xffffff,
shininess: 250,
side: THREE.DoubleSide,
vertexColors: true,
transparent: true
});
const mesh = new THREE.Mesh(geometry, material);
return mesh;
}