Public
Edited
Sep 30, 2023
Importers
Insert cell
Insert cell
Insert cell
BABYLON = (await import("https://esm.sh/babylonjs@6")).default
Insert cell
MATERIALS = (await import("https://esm.sh/babylonjs-materials@6")).default
Insert cell
GUI = (await import("https://esm.sh/babylonjs-gui@6")).default
Insert cell
function init(id) {
const canvas = document.getElementById(id);
const engine = new BABYLON.Engine(canvas, true);
const scene = new BABYLON.Scene(engine);
scene.useRightHandedSystem = true;
const camera = new BABYLON.ArcRotateCamera("cam", 0,0,1, BABYLON.Vector3.Zero(), scene);
camera.setPosition( new BABYLON.Vector3(0,-5,25) );
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(3,3,1), scene);
return {canvas, engine, scene, camera, light};
}
Insert cell
function twodgrid(scene, width=20, height=20, name="box") {
const box = BABYLON.MeshBuilder.CreateBox(
name,
{width:width, height:height, depth:.2},
scene);
const boxmat = new MATERIALS.GridMaterial(`${name}_mat`, scene);
boxmat.majorUnitFrequency = 2;
boxmat.useMaxLine = false;

box.material = boxmat;

return {box, boxmat};
}
Insert cell
function spinsphere(scene, x,y,z=0,
red=1,green=0,blue=0, radius=.2,
whiteLines=false, spin=true) {
const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter:radius*2});
const spheremat = new MATERIALS.GridMaterial("spheremat", scene);
spheremat.mainColor = new BABYLON.Color3(red,green,blue);
sphere.material = spheremat;
sphere.position.set(x,y,z);

if (whiteLines) { spheremat.lineColor = new BABYLON.Color3(1,1,1); }

if (spin) {
scene.registerBeforeRender( function() {
sphere.rotate(new BABYLON.Vector3(0,1,1), .01);
});
}

return {sphere, spheremat};
}
Insert cell
function gui(scene, camera) {
const ui = GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI", true, scene);
const panel = new GUI.StackPanel();
panel.width = "150px";
panel.height = "100px";
panel.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
panel.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_BOTTOM;
ui.addControl(panel);

const zout = GUI.Button.CreateSimpleButton("out", "Zoom Out");
zout.background = "green";
zout.color = "black";
zout.width = "150px";
zout.height = "50px";
zout.onPointerUpObservable.add(function () {
camera.radius = camera.radius - 5;
});

const zin = GUI.Button.CreateSimpleButton("in", "Zoom In");
zin.background = "green";
zin.color = "black";
zin.width = "150px";
zin.height = "50px";
zin.onPointerUpObservable.add(function () {
camera.radius = camera.radius + 5;
});

panel.addControl(zout);
panel.addControl(zin);
}
Insert cell
textbox = function(width,height,
x=0,y=0,z=0,
color="black") {
const plane = BABYLON.MeshBuilder.CreatePlane( "plane", {width:width,height:height} );
plane.position.set(x,y,z);

const texture = BABYLON.AdvancedDynamicTexture.CreateForMesh(plane, 30,50);
const container = new BABYLON.Rectangle();
container.thickness = .1;
container.background = color;
container.alpha = .7;

texture.addControl(container);
return {plane,texture,container};
}
Insert cell
planeFromPtVec = function(scene,
ox=0,oy=0,oz=0,
vx=0,vy=0,vz=1) {
const plane = BABYLON.MeshBuilder.CreatePlane("PLANE", {size:10, sideOrientation:BABYLON.Mesh.DOUBLESIDE}, scene);
// The normal to the plane is <0,0,1>. Rotate it to the desired normal by
// first finding their cross: <0,0,1> x <vx,vy,vz> = <-vy, vx, 0>.
const axis = new BABYLON.Vector3(-vy, vx, 0);
// Use this to rotate through the appropriate angle.
const angle = Math.acos( vz/Math.sqrt(vx*vx+vy*vy+vz*vz) );
plane.rotate(axis, angle, BABYLON.Space.WORLD);

plane.position.set(ox,oy,oz);

const planemat = new BABYLON.GridMaterial("planemat", scene);
planemat.lineColor = new BABYLON.Color3(1,1,1);
planemat.mainColor = new BABYLON.Color3(1,1,1);
planemat.opacity = .9;
plane.material = planemat;

return {plane,planemat}
}
Insert cell
getAngleBetween = function(v1, v2) {
const ctheta = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)/(v1.length()*v2.length());
return ( Math.acos(ctheta) );
}
Insert cell
function arrowBuilder(scene, length, thickness) {
// Build an arrow, returns the mesh, material, start, and end.
// This is intented to be used to build an arrow and
const shaft = BABYLON.MeshBuilder.CreateCylinder("shaft", {height:length*4/5, diameter:thickness}, scene);
const head = BABYLON.MeshBuilder.CreateCylinder("head", {height:length/5, diameterBottom:1.5*thickness, diameterTop:0}, scene);
head.position.set(0, length/2 , 0);
const arrow = BABYLON.Mesh.MergeMeshes([shaft,head]);
if (arrow == null) { throw new Error("Arrow null. "); }
const arrowmat = new BABYLON.StandardMaterial("arrowmat", scene);
arrowmat.ambientColor = new BABYLON.Color3(1, 0, 0);
arrow.material = arrowmat;
return {arrow,arrowmat};
}
Insert cell
function arrowStartEnd(arrow) {
}
Insert cell
function setArrowStart(arrow, svec) {
// Keep the arrow direct constant, but set the start position
}
Insert cell
arrowBuilder = function( scene, params ) {
const start = [params.startx, params.starty, params.startz];
const end = [params.endx, params.endy, params.endz];
const dir = [params.dirx, params.diry, params.dirz];

let all = function(a: (number|undefined)[]) {
for (let i=0; i<a.length; i++) {
if (a[i] == undefined) {
return 0;
}
}
return 1;
}
const s = all(start)+all(end)+all(dir);

if ( s == 3 ) { throw new Error("Arrow start&end&dir defined -- must define precisely two"); }
if ( s<2 ) { throw new Error("Arrow <2 defined -- must define precisely two"); }

if (all(start) && all(dir)) {
for (let i=0; i<2; i++) {
end[i] = (start[i] || 0) + (dir[i] || 0);
}
}
if (all(end) && all(dir)) {
for (let i=0; i<2; i++) {
start[i] = (end[i] || 0) - (dir[i] || 0);
}
}
if (all(start) && all(end)) {
for (let i=0; i<2; i++) {
dir[i] = (end[i] || 0) - (start[i] || 0);
}
}

const thicknum = (params.thick || 1);
const rnum = (params.colorr || 1);
const gnum = (params.colorg || .5);
const bnum = (params.colorb || .5);
const dv = new Vector3(dir[0]||0, dir[1]||0, dir[2]||0);
const sv = new Vector3(start[0]||0, start[1]||0, start[2]||0);

const shaft = MeshBuilder.CreateCylinder("shaft", {height:dv.length()*4/5, diameter:thicknum}, scene);
const head = MeshBuilder.CreateCylinder("head", {height:dv.length()/5, diameterBottom:1.5*thicknum, diameterTop:0}, scene);
head.position.set(0, dv.length()/2 , 0);
const arrow = Mesh.MergeMeshes([shaft,head]);
if (arrow == null) { throw new Error("Arrow null. "); }
const arrowmat = new StandardMaterial("arrowmat", scene);
arrowmat.ambientColor = new Color3(rnum, gnum, bnum);
arrow.material = arrowmat;

const rotax = new Vector3(dir[2]||0, 0, -(dir[0]||0));
const angle = getAngleBetween(new Vector3(0,1,0), dv);
arrow.rotate(rotax, angle, Space.WORLD);

arrow.position = sv.add(dv.scale(.5));
return {arrow,arrowmat};
}
Insert cell
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