Published
Edited
Jul 17, 2021
4 forks
73 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
ln = import('https://unpkg.com/@lnjs/core@0.5.0/es/index.js?module')
Insert cell
Insert cell
scene = new ln.Scene()
Insert cell
Insert cell
cube = {
// A "Cube" in ln is defined by the position of 2 points
// We are using vectors to store the X, Y and Z coordinates of a position in 3D space
const min = new ln.Vector(-1, -1, -1)
const max = new ln.Vector(1, 1, 1)
return new ln.Cube(min, max)
}
Insert cell
Insert cell
Insert cell
eye = new ln.Vector(3, 2, 4)
Insert cell
center = new ln.Vector(0, 0, 0)
Insert cell
up = new ln.Vector(0, 1, 0)
Insert cell
Insert cell
width // corresponds to the width of the notebook
Insert cell
height = 500
Insert cell
Insert cell
fovy = 40 // vertical field of view, in degrees
Insert cell
znear = 0.1 // near z plane
Insert cell
zfar = 10 // far z plane
Insert cell
Insert cell
step = 0.01 // how finely to chop the paths for visibility testing
Insert cell
Insert cell
paths = {
scene.add(cube)
return scene.render(eye, center, up, width, height, fovy, znear, zfar, step)
}
Insert cell
Insert cell
svg`${ln.toSVG(paths, width, height)}` // we are using the `svg` utility from Observable to render our SVG
Insert cell
Insert cell
Insert cell
await visibility(), (() => {
// create a scene
const scene = new ln.Scene()
// create a box
const min = new ln.Vector(-1, -1, -1)
const max = new ln.Vector(1, 1, 1)
const cube = new ln.Cube(min, max)
// we can specify which paths to render and create new ones:
cube.paths = function() {
const { x: x1, y: y1, z: z1 } = this.min
const { x: x2, y: y2, z: z2 } = this.max
const paths = []
const N = 10
for(let i = 0; i <= N; i++) {
const p = i / N
const x = x1 + (x2 - x1) * p
const y = y1 + (y2 - y1) * p
const z = z1 + (z2 - z1) * p
paths.push([new ln.Vector(x, y2, z1), new ln.Vector(x, y2, z2)]) // on top face
paths.push([new ln.Vector(x2, y, z2), new ln.Vector(x1, y, z2)]) // on front face
paths.push([new ln.Vector(x2, y1, z), new ln.Vector(x2, y2, z)]) // on right face
// uncomment these for more paths :)
// paths.push([new ln.Vector(x1, y2, z), new ln.Vector(x2, y2, z)]) // on top face
// paths.push([new ln.Vector(x, y1, z2), new ln.Vector(x, y2, z2)]) // on front face
// paths.push([new ln.Vector(x2, y, z1), new ln.Vector(x2, y, z2)]) // on right face
}
return paths
}
scene.add(cube)

let eye = new ln.Vector(3, 2, 4)
let center = new ln.Vector(0, 0, 0)
let up = new ln.Vector(0, 1, 0)

const height = 500
let paths = scene.render(eye, center, up, width, height, 40, 0.1, 100, 0.01)
return svg`${ln.toSVG(paths, width, height)}`
})()
Insert cell
Insert cell
await visibility(), (() => {
// creating a Scene
const scene = new ln.Scene()
// create a function to return the paths for a given cube given the number on each axis
const getPaths = (cube, nx = 10, ny = 10, nz =10) => () => {
const paths = []
const { x: x1, y: y1, z: z1 } = cube.min
const { x: x2, y: y2, z: z2 } = cube.max
paths.push([new ln.Vector(x1, y1, z1), new ln.Vector(x1, y2, z1)])
paths.push([new ln.Vector(x1, y1, z1), new ln.Vector(x1, y1, z2)])
paths.push([new ln.Vector(x2, y2, z2), new ln.Vector(x1, y2, z2)])
paths.push([new ln.Vector(x2, y2, z2), new ln.Vector(x2, y1, z2)])
paths.push([new ln.Vector(x2, y2, z2), new ln.Vector(x2, y2, z1)])
paths.push([new ln.Vector(x1, y2, z1), new ln.Vector(x2, y2, z1)])
paths.push([new ln.Vector(x1, y2, z1), new ln.Vector(x1, y2, z2)])
paths.push([new ln.Vector(x1, y2, z2), new ln.Vector(x1, y1, z2)])
paths.push([new ln.Vector(x1, y1, z2), new ln.Vector(x2, y1, z2)])
for(let i = 1; i < nx; i++) {
const p = i / nx
const x = x1 + (x2 - x1) * p
paths.push([new ln.Vector(x, y1, z2), new ln.Vector(x, y2, z2)])
paths.push([new ln.Vector(x, y1, z1), new ln.Vector(x, y1, z2)])
paths.push([new ln.Vector(x, y2, z1), new ln.Vector(x, y2, z2)])
}
for(let i = 1; i < ny; i++) {
const p = i / ny
const y = y1 + (y2 - y1) * p
paths.push([new ln.Vector(x1, y, z2), new ln.Vector(x2, y, z2)])
paths.push([new ln.Vector(x1, y, z1), new ln.Vector(x1, y, z2)])
}
for(let i = 1; i < nz; i++) {
const p = i / nz
const z = z1 + (z2 - z1) * p
paths.push([new ln.Vector(x1, y2, z), new ln.Vector(x2, y2, z)])
paths.push([new ln.Vector(x1, y1, z), new ln.Vector(x1, y2, z)])
}
return paths
}
// create a cube at the center
let cube = new ln.Cube(new ln.Vector(-2, -2, -2), new ln.Vector(2, 2, 2))
cube.paths = getPaths(cube, 30, 30, 30)
scene.add(cube)
// let's add 200 randomly positioned boxes
for(let i = 0; i < 200; i ++) {
let length = randInt(1, 30)
let p = (randInt(30) + length/4) * coin() // pos on axis
let w = random(.08, 1.5)
let offset1 = random(1.9 - w/2) * coin()
let offset2 = random(1.9 - w/2) * coin()
let min, max
let axis = randInt(3)
if(axis === 0) { // x axis
min = new ln.Vector(p - length / 2, offset1 - w/2, offset2 - w/2)
max = new ln.Vector(p + length / 2, offset1 + w/2, offset2 + w/2)
}
else if(axis === 1) { // y axis
min = new ln.Vector(offset1 - w/2, p - length / 2, offset2 - w/2)
max = new ln.Vector(offset1 + w/2, p + length / 2, offset2 + w/2)
}
else { // z axis
min = new ln.Vector(offset1 - w/2, offset2 - w/2, p - length / 2)
max = new ln.Vector(offset1 + w/2, offset2 + w/2, p + length / 2)
}
const box = new ln.Cube(min, max)
const diffx = max.x - min.x
const diffy = max.y - min.y
const diffz = max.z - min.z
box.paths = getPaths(box, random(1, 15) * diffx | 0, random(1, 15) * diffy | 0, random(1, 15) * diffz | 0)

scene.add(box)
}
// render the scene
const eye = new ln.Vector(-28, 12, 8)
const center = new ln.Vector(-3, 0, 0)
const height = width / 21 * 29.7
const angle = Math.PI / 2. + .5
const up = new ln.Vector(-.3, 1.2, -.6).normalize()
// compute the paths to show
const paths = scene.render(eye, center, up, width, height, 40, 0.1, 150, 0.01)
// display the SVG
return svg`${ln.toSVG(paths, width, height)}`
})()
Insert cell
Insert cell
Insert cell
Insert cell
await visibility(), (() => {
const scene = new ln.Scene()
// first, we create a Sphere centered on (0, 0, 0) with a diameter of 1.1
const a = new ln.Sphere(new ln.Vector(0, 0, 0), 1.1)
// then we create a Cube as we did previously and define its paths
const b = new ln.Cube(new ln.Vector(-.8, -.8, -.8), new ln.Vector(.8, .8, .8))
b.paths = () => {
const paths = []
const { x: x1, y: y1, z: z1 } = b.min
const { x: x2, y: y2, z: z2 } = b.max
for(let i = 0; i <= 30; i++) {
const p = i / 30
const x = x1 + (x2 - x1) * p
const y = y1 + (y2 - y1) * p
const z = z1 + (z2 - z1) * p
paths.push([new ln.Vector(x, y2, z1), new ln.Vector(x, y2, z2)])
paths.push([new ln.Vector(x2, y, z1), new ln.Vector(x2, y, z2)])
paths.push([new ln.Vector(x2, y, z2), new ln.Vector(x1, y, z2)])
}
return paths
}
// now we create a Cylinder they are always centered on (0, 0, 0)
const c = new ln.Cylinder(0.6, -2, 2)
// here we are creating copies of the cylinder 'c' applying a transformation to it
// in this case a rotation of Math.PI/2 (= 90°) around the X axis (1, 0, 0) for the first one
const d = new ln.TransformedShape(c, ln.rotate(new ln.Vector(1, 0, 0), Math.PI/2))
// in this case a rotation of Math.PI/2 (= 90°) around the Y axis (0, 1, 0) for the second one
const e = new ln.TransformedShape(c, ln.rotate(new ln.Vector(0, 1, 0), Math.PI/2))

// Finally, we can create a BooleanShape using CSG operation
// (Sphere & Cube) - CylinderA - CylinderB - Cylinder
let result = new ln.BooleanShape(ln.CSGOperation.Intersection, a, b) // intersection of 'a' and 'b'
result = new ln.BooleanShape(ln.CSGOperation.Difference, result, c) // remove the first cylinder
result = new ln.BooleanShape(ln.CSGOperation.Difference, result, d) // remove the second cylinder
result = new ln.BooleanShape(ln.CSGOperation.Difference, result, e) // remove the third cylinder

scene.add(result)

// define camera parameters
const eye = new ln.Vector(1.1, 2.2, 1.2) // camera position
const center = new ln.Vector(0, 0, -0.2) // camera looks at
const up = new ln.Vector(0, 0, 1) // up direction

// define rendering parameters
const height = 500 // view height
const fovy = 50 // vertical field of view, degrees
const znear = 0.1 // near z plane
const zfar = 10 // far z plane
const step = 0.01 // how finely to chop the paths for visibility testing

const paths = scene.render(eye, center, up, width, height, fovy, znear, zfar, step)
return svg`${ln.toSVG(paths, width, height)}`
})()
Insert cell
Insert cell
await visibility(), (() => {
function shape(x, y) {
return -1 / (x ** 2 + y ** 2) ** 2
}

const scene = new ln.Scene()
const min = new ln.Vector(-2, -2, -4)
const max = new ln.Vector(2, 2, 2)
const box = new ln.Box(min, max)
const fn = new ln.Function(shape, box, ln.Direction.Below)
scene.add(fn)

const eye = new ln.Vector(3, 0, 3)
const center = new ln.Vector(1, 0, 0)
const up = new ln.Vector(0, 0, 1)
const height = 500
const paths = scene.render(eye, center, up, width, height, 50, 0.1, 100, 0.01)
return svg`${ln.toSVG(paths, width, height)}`
})()
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more