Published
Edited
Jul 22, 2021
8 stars
Insert cell
Insert cell
{
const scene = new THREE.Scene();
const renderer = new THREE.WebGLRenderer({ antialias: true });
const camera = new THREE.PerspectiveCamera(45, 1 / 0.8, 0.1, 1000);
camera.position.set(1, 1, 1.8);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
invalidation.then(() => (controls.dispose(), renderer.dispose()));

renderer.setSize(width, width * 0.8);
renderer.setClearColor(0xffffff);
renderer.setPixelRatio(window.devicePixelRatio);
controls.addEventListener("change", () => renderer.render(scene, camera));

const points = getPoints(350);
const pointsDrawn = drawPoints(scene, points);

const convHull = new THREE.ConvexHull().setFromPoints(points);
drawHull(scene, convHull);

renderer.render(scene, camera);
yield renderer.domElement;
}
Insert cell
function drawHull(scene, convHull) {
for (let face of convHull.faces) {
if (face.normal.z > 0) continue;

const shouldHighlight = Math.random() < 0.02;
const highlightColor = new THREE.Color(0.2, 0.2, 0.8).lerp(
new THREE.Color(1, 1, 1),
0.5
);

const col = 0.5 + Math.random() / 2;
const material = new THREE.MeshBasicMaterial({
color: shouldHighlight
? highlightColor
: new THREE.Color(
0.5 + Math.random() / 2,
0.5 + Math.random() / 2,
0.5 + Math.random() / 2
),
side: THREE.DoubleSide
});
const geometry = new THREE.BufferGeometry();

const p1 = face.getEdge(0).vertex.point;
const p2 = face.getEdge(1).vertex.point;
const p3 = face.getEdge(2).vertex.point;
const vertices = new Float32Array([
p1.x,
p1.z,
p1.y,
p2.x,
p2.z,
p2.y,
p3.x,
p3.z,
p3.y
]);
geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

const materialClone = new THREE.MeshBasicMaterial({
color: "black",
wireframe: true,
side: THREE.DoubleSide
});
const geometryClone = new THREE.BufferGeometry();
const verticesClone = new Float32Array([
p1.x,
p1.z,
p1.y,
p2.x,
p2.z,
p2.y,
p3.x,
p3.z,
p3.y
]);
geometryClone.setAttribute(
"position",
new THREE.BufferAttribute(verticesClone, 3)
);
const meshClone = new THREE.Mesh(geometryClone, materialClone);
scene.add(meshClone);

const materialPlane = new THREE.MeshBasicMaterial({
color: shouldHighlight ? highlightColor : "black",
wireframe: true
});
const geometryPlane = new THREE.BufferGeometry();
const verticesPlane = new Float32Array([
p1.x,
0,
p1.y,
p2.x,
0,
p2.y,
p3.x,
0,
p3.y
]);
geometryPlane.setAttribute(
"position",
new THREE.BufferAttribute(verticesPlane, 3)
);
const meshPlane = new THREE.Mesh(geometryPlane, materialPlane);
scene.add(meshPlane);

const materialPlaneClone = new THREE.MeshBasicMaterial({
color: shouldHighlight ? highlightColor : "white",
wireframe: false
});
const geometryPlaneClone = new THREE.BufferGeometry();
const verticesPlaneClone = new Float32Array([
p1.x,
0,
p1.y,
p2.x,
0,
p2.y,
p3.x,
0,
p3.y
]);
geometryPlaneClone.setAttribute(
"position",
new THREE.BufferAttribute(verticesPlaneClone, 3)
);
const meshPlaneClone = new THREE.Mesh(
geometryPlaneClone,
materialPlaneClone
);
scene.add(meshPlaneClone);

// highlight some faces
if (shouldHighlight) {
const lineMaterial = new THREE.LineBasicMaterial({
color: highlightColor
});
const lineGeometry1 = new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(p1.x, p1.z, p1.y),
new THREE.Vector3(p1.x, 0, p1.y)
]);
const line1 = new THREE.Line(lineGeometry1, lineMaterial);
scene.add(line1);

const lineGeometry2 = new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(p2.x, p2.z, p2.y),
new THREE.Vector3(p2.x, 0, p2.y)
]);
const line2 = new THREE.Line(lineGeometry2, lineMaterial);
scene.add(line2);

const lineGeometry3 = new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(p3.x, p3.z, p3.y),
new THREE.Vector3(p3.x, 0, p3.y)
]);
const line3 = new THREE.Line(lineGeometry3, lineMaterial);
scene.add(line3);
}
}
}
Insert cell
function drawPoints(scene, points) {
const res = [];
const wh = 0.005;
for (let point of points) {
const pointMaterial = new THREE.MeshBasicMaterial({
color: new THREE.Color(0, 0, 0)
});
const pointGeometry = new THREE.BoxGeometry(wh, wh, wh);
const pointFlat = new THREE.Mesh(pointGeometry, pointMaterial);
pointFlat.position.set(point.x, 0, point.y);
scene.add(pointFlat);
res.push(pointFlat);

const liftedPointmaterial = new THREE.MeshBasicMaterial({
color: new THREE.Color(0.0, 0.0, 0.8)
});
const liftedPointgeometry = new THREE.BoxGeometry(wh, wh, wh);
const liftedPoint = new THREE.Mesh(
liftedPointgeometry,
liftedPointmaterial
);
liftedPoint.position.set(point.x, point.z, point.y);
scene.add(liftedPoint);
}
return res;
}
Insert cell
function getPoints(n) {
const w = 1;
const res = [];
for (let i = 0; i < n; ++i) {
const [x, y] = [-w / 2 + Math.random() * w, -w / 2 + Math.random() * w];
res.push(new THREE.Vector3(x, y, 0.35 + x ** 2 + y ** 2));
}
return res;
}
Insert cell
THREE = {
const THREE = (window.THREE = await require("three@0.129.0/build/three.min.js"));
await require("three@0.129.0/examples/js/controls/OrbitControls.js").catch(
() => {}
);
await require("three@0.129.0/examples/js/math/ConvexHull.js").catch(() => {});
return THREE;
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more