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

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