Published
Edited
Oct 20, 2020
1 star
Insert cell
Insert cell
md`See chart() function under picture.`
Insert cell
chart = {
const svg = d3.create("svg")
.attr("height", 512)
.attr("width", 512)
.attr('viewBox', "-256 -256 512 512")
.attr('style', 'background: #25321E')
// every point contains trace
let points = (new Array(2048)).fill(0).map(p => {
let x = (Math.random()-.5)*512
let y = (Math.random()-.5)*512
let z = (Math.random()-.5)*512
let source = new THREE.Vector3(x, y, z);
// At first, target is closest point on surface.
let distance, norm, diff, target
target = source.clone()
for(let i = 0; i < 3; i++) {
distance = mpa(source, new THREE.Vector3(0, 0, 0));
norm = normal(source).multiplyScalar(-1);
diff = norm.clone().multiplyScalar(distance);
source = target.clone();
target.add(diff);
if(Math.abs(distance)<1e-10){
let slideDistance = 10;
let slide = norm.clone().applyAxisAngle(new THREE.Vector3(0,0,1), 3.1415/2).multiplyScalar(slideDistance);
target.add(slide);
}
}
// But slide target along surface (along tangent).
// The closer we were, the harder we slide (but no shorter than certain constant).
// let slideDistance = .00001;
// if (distance > 0) {
// slideDistance = slideDistance + (512 / distance) + (128 / Math.sqrt(distance));
// }
// let slide = norm.clone().applyAxisAngle(new THREE.Vector3(0,0,1), 3.1415/2).multiplyScalar(slideDistance);
// target.add(slide);
let destination = target.clone();
// // Mutation demo, remove to see the main purpose of code above.
// // Wrong approach:
// let begin = new THREE.Vector3(0,0,0)
// let end = begin.add(new THREE.Vector3(64,64,64))
// // Right approach (uncomment it to observe result):
// begin = new THREE.Vector3(0,0,0)
// end = begin.clone().add(new THREE.Vector3(64,64,64))
// return [begin, end]
// // End of mutation demo
return [source, destination]
})
// drawing traces
svg.selectAll('line')
.data( points )
.enter()
.append('line')
.attr('x1', p => p[0].x)
.attr('y1', p => p[0].y)
.attr('x2', p => p[1].x)
.attr('y2', p => p[1].y)
.attr('stroke', p => mpa(p[0], new THREE.Vector3(0, 0, 0)) < 0 ? '#00A1BC' : '#FCE000')
.attr('stroke-width', 1)

// drawing points
svg.selectAll('circle')
.data( points )
.enter()
.append('circle')
.attr('cx', p => p[1].x)
.attr('cy', p => p[1].y)
.attr('r', p => 1)
.attr('fill', p => mp(p[0]) < 0 ? '#00A1BC' : '#FCE000')

return svg.node()
}
Insert cell
normal = p => {
p = p.clone();
let n = new THREE.Vector3(mpa(p, new THREE.Vector3(+.01, 0, 0))
- mpa(p, new THREE.Vector3(-.01, 0, 0)),
mpa(p, new THREE.Vector3(0, +.01, 0))
- mpa(p, new THREE.Vector3(0, -.01, 0)),
mpa(p, new THREE.Vector3(0, 0, +.01))
- mpa(p, new THREE.Vector3(0, 0, -.01)))
n = n.normalize()
return n
}
Insert cell
mpa = (p, d) => {
p = p.clone();
d = d.clone();
p.add(d);
// rotation of the shape
p = p.clone()
.applyAxisAngle(new THREE.Vector3(0,1,0).normalize(), time / 32)
.normalize()
.multiplyScalar(p.length())

//sphere
//return p.length()-200
// torus
let q = new THREE.Vector2(new THREE.Vector2(p.x, p.y).length()-120,p.z)
return q.length()-60
};

//zero = new THREE.Vector3(0, 0, 0);
//mp = p => mpa(p, new THREE.Vector3(0, 0, 0));
Insert cell
mp = p => mpa(p, new THREE.Vector3(0, 0, 0));
Insert cell
time = {
let time = 0
while (true) {
yield time+=100
await Promises.tick(100)
}
}
Insert cell
// viewof octaves = slider({
// min: 1,
// max: 6,
// step: 1,
// value: 3,
// format: v => `${v} ${v === 1 ? "octave" : "octaves"}`,
// title: "Noise detail"
// })
import {slider, select} from "@jashkenas/inputs"
Insert cell
THREE = require("three@0.89.0/build/three.min.js")
Insert cell
d3 = require('d3@5')
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