Public
Edited
Apr 17, 2023
Insert cell
Insert cell
// https://observablehq.com/@jashkenas/inputs
// https://threejs.org/docs/#api/en/geometries/TextGeometry
// https://threejs.org/docs/#api/en/geometries/ExtrudeGeometry
// import {coordinates} from "@jashkenas/inputs"
// https://github.com/mrdoob/three.js/tree/master/examples/fonts
// https://gero3.github.io/facetype.js/
//
Insert cell
Insert cell
Insert cell
Insert cell
// textGeometry = new THREE.TextGeometry(object.message, { font: font_shapes, size: object.size, height: object.depth } );
Insert cell
Insert cell
meshB = await icon(svgRouter, 0)
Insert cell
meshRes = {
const material = new THREE.MeshNormalMaterial();
let meshA = extrudeText
// meshB.position.add(new THREE.Vector3( 0.5, 0.5, 0.5) )
meshA.updateMatrix()
meshB.updateMatrix()
let bspA = CSG.fromMesh( meshA )
let bspB = CSG.fromMesh( meshB )
let bspResult = bspA.subtract(bspB)
let meshRes = CSG.toMesh( bspResult, meshA.matrix, meshA.material )
meshRes.material = material
return meshRes
}
Insert cell
{
const renderer = new THREE.WebGLRenderer({ antialias: true });
const controls = new THREE.OrbitControls(camera, renderer.domElement);
invalidation.then(() => (controls.dispose(), renderer.dispose()));
renderer.setSize(width, 600);
renderer.setPixelRatio(devicePixelRatio);
controls.addEventListener("change", () => renderer.render(scene, camera));
renderer.render(scene, camera);
return renderer.domElement;
}
Insert cell
Insert cell
Insert cell
async function icon(iconPath, z) {
const material = new THREE.MeshNormalMaterial();
const geometry = await createIconGeometryFromSVGPath(iconPath);
const icon = new THREE.Mesh(geometry, material);
icon.position.set(0, 0, z);
return icon;
}
Insert cell
async function createIconGeometryFromSVGPath(pathStr) {
const ICON_SIZE = 5;
const ICON_DEPTH = 5
const svgUrl = URL.createObjectURL( new Blob([`<svg><path d="${pathStr}"/></svg>`], { type: 'image/svg+xml' }) );
const loader = new THREE.SVGLoader();
const data = await new Promise(resolve => { loader.load(svgUrl, resolve); });
const shapes = data.paths[0].toShapes(false);
URL.revokeObjectURL(svgUrl);

// Extrude to an array of geometries.
const shapeBufferGeometry = [];
shapes.forEach(shape =>
shapeBufferGeometry.push(
new THREE.ExtrudeBufferGeometry(shape, { depth: ICON_DEPTH, bevelEnabled: false })
)
);

// Merge the geometries (for performance)
const iconGeometry = THREE.BufferGeometryUtils.mergeBufferGeometries( shapeBufferGeometry, true );

// _try_ to 'normalise' the icon size.
iconGeometry.computeBoundingSphere();
iconGeometry.scale(
ICON_SIZE / iconGeometry.boundingSphere.radius,
ICON_SIZE / iconGeometry.boundingSphere.radius,
1
);

return iconGeometry;
}
Insert cell
scene = {
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x333333);
scene.add(new THREE.AxesHelper(100));
// scene.add( extrudeText );
// scene.add( plane );
//scene.add(meshB)
scene.add(meshRes)
return scene;
}
Insert cell
camera = {
const fov = 45;
const aspect = width / 600;
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(10, 10, 100);
camera.up.set(0, 1, 0);
camera.lookAt(0, 0, 0);
camera.updateMatrixWorld();
return camera;
}
Insert cell
THREE = {
var version = '0.123'
const THREE = await require(`three@${version}/build/three.min.js`);
THREE.SVGLoader = await import(`https://unpkg.com/three@${version}/examples/jsm/loaders/SVGLoader.js`).then(
a => a.SVGLoader
);
THREE.OrbitControls = await import(`https://unpkg.com/three@${version}/examples/jsm/controls/OrbitControls.js`).then(
a => a.OrbitControls
);
THREE.BufferGeometryUtils = await import(`https://unpkg.com/three@${version}/examples/jsm/utils/BufferGeometryUtils.js`).then(
a => a.BufferGeometryUtils
);
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