Published
Edited
Sep 5, 2020
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
sweptSurface = {
const { nsweep, nsection, closed, frenet } = conf;
//initial curves and twist declaration
const start = curve(views.start).getPoints(nsection);
const finish = curve(views.finish).getPoints(nsection);
const sweepCurve = curve(views.sweep).getPoints(nsweep);
const twist = curve(views.sweep).computeFrenetFrames(nsweep, closed);
const curvesArray = [];
//twisting
for (const sweepPoint of Array(nsweep + 1).keys()) {
const angle = THREE.MathUtils.degToRad(conf.twist) * (sweepPoint / nsweep);
twist.binormals[sweepPoint].applyAxisAngle(twist.tangents[sweepPoint], angle);
twist.normals[sweepPoint].applyAxisAngle(twist.tangents[sweepPoint], angle);
}
//sweeping
for (const sweepPoint of Array(nsweep + 1).keys()) {
const curve = [];
const matrix = new THREE.Matrix4();
matrix.makeBasis(twist.binormals[sweepPoint], twist.normals[sweepPoint], twist.tangents[sweepPoint]);
for (const section of Array(nsection).keys()) {
const coord = new THREE.Vector3();
coord.lerpVectors(start[section], finish[section], sweepPoint / nsweep);
coord.applyMatrix4(matrix);
coord.add(sweepCurve[sweepPoint]);
curve.push(coord);
}
curvesArray.push(curve);
}
//draw frenet vectors
frenetFrames.children = [];
if (frenet) {
for (const sweepPoint of Array(nsweep + 1).keys()) {
const vectors = [
new THREE.ArrowHelper(twist.tangents[sweepPoint], sweepCurve[sweepPoint], 1, 0xff0000),
new THREE.ArrowHelper(twist.normals[sweepPoint], sweepCurve[sweepPoint], 1, 0x00ff00),
new THREE.ArrowHelper(twist.binormals[sweepPoint], sweepCurve[sweepPoint], 1, 0x0000ff)
];

for (const vector of vectors) {
frenetFrames.add(vector);
}
}
}
return curvesArray;
}
Insert cell
//
// Simple trackball controls. Changes the surface rotation and the camera distance in
// response to mouse interactions in the 3D view
//
{
let mouse = null;
let canvas = renderer.domElement;
canvas.oncontextmenu = (e) => {
e.preventDefault();
e.stopPropagation()
}
canvas.onmousedown = (e) => {
mouse = new THREE.Vector3(e.offsetX,e.offsetY,0)
}
canvas.onmouseup = (e) => {
mouse = null
}
canvas.onmousemove = (e) => {
if (mouse) {
let newMouse = new THREE.Vector3(e.offsetX,e.offsetY,0);
if (e.buttons & 1) {
let axis = new THREE.Vector3(newMouse.y-mouse.y,newMouse.x-mouse.x,0);
let angle = axis.length()*Math.PI/180*0.5; // 1/2 degree
if (angle == 0) return;
axis.normalize();
let rot = new THREE.Quaternion().setFromAxisAngle(axis,angle);
surface.quaternion.premultiply(rot);
frenetFrames.quaternion.premultiply(rot);
}
else {
let scale = newMouse.y > mouse.y ? 0.98 : (newMouse.y < mouse.y ? 1.02 : 1);
camera.position.multiplyScalar(scale);
}
renderer.render(scene,camera)
mouse = newMouse
}
}
}
Insert cell
//
// Reevaluates the surface geometry. Called whenever the interface curves change
//
{
surface.geometry.dispose();
surface.geometry = surfaceGeometry (sweptSurface);
renderer.render(scene,camera)
}
Insert cell
//
// Toggles the wireframe / texture settings in the material
//
{
material.wireframe = !!conf.wireframe;
material.map = conf.texture ? checkerTexture : null;
material.needsUpdate = true;
renderer.render(scene,camera)
}
Insert cell
Insert cell
renderer = new THREE.WebGLRenderer ({canvas, antialias: true})
Insert cell
camera = {
let camera = new THREE.PerspectiveCamera(70, 16/9, 0.1, 100);
camera.position.set (0,0,6)
return camera;
}
Insert cell
light = {
let lights = new THREE.Group();
let lightDir = new THREE.DirectionalLight (0xffffff, 1);
lightDir.position.set(1,1,1);
let lightAmb = new THREE.AmbientLight (0x303030);
lights.add (lightAmb, lightDir)
return lights
}
Insert cell
material = new THREE.MeshStandardMaterial({color: 0xff0000, side:THREE.DoubleSide })
Insert cell
surface = new THREE.Mesh (new THREE.Geometry(), material)
Insert cell
frenetFrames = new THREE.Group()
Insert cell
objects = new THREE.Group().add(surface,frenetFrames)
Insert cell
scene = {
let scene = new THREE.Scene();
scene.background = new THREE.Color(0xEEEEEE);
scene.add(objects)
scene.add(light)
return scene
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
checkerTexture = {
let texture = new THREE.Texture (checker);
texture.anisotropy = 4;
texture.minFilter = THREE.LinearFilter
texture.needsUpdate = true;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
return texture;
}
Insert cell
Insert cell
//
// Returns a THREE.Geometry consisting of a mesh connecting polylines
//
function surfaceGeometry (polylines) {
let geometry = new THREE.Geometry();
let n = polylines[0].length;
let m = polylines.length;
let uvs = [];
let tex = (i,j) => new THREE.Vector2 (i/(m-1),j/(n-1));
geometry.faceVertexUvs = [uvs];
for (let poly of polylines)
for (let v of poly) geometry.vertices.push(v);
for (let i = 0; i+1 < m; i++) {
for (let j = 0; j+1 < n; j++) {
geometry.faces.push (new THREE.Face3(i*n+j, i*n+j+1, (i+1)*n+j),
new THREE.Face3((i+1)*n+j, i*n+j+1, (i+1)*n+j+1));
uvs.push ([tex(i, j), tex(i, j+1), tex(i+1, j)],
[tex(i+1, j), tex(i, j+1), tex(i+1, j+1)]);
}
}
geometry.computeFaceNormals();
geometry.computeVertexNormals();
return geometry
}
Insert cell
// Shortcut for creating a Vector3
Vec = (x,y,z=0) => new THREE.Vector3 (x,y,z)
Insert cell
//
// Returns a Matrix4 that maps a centered cube of size side to a rectangle of width w and height h.
// By default, isometric xy projection is used, but one can also choose yz or zx projection.
//
canvasTransform = function (side, w, h, projection = 'xy') {
let scale = Math.min(w,h)/side;
let angles = projection == 'xy' ? [0,0,0] : (projection == 'yz' ?
[-Math.PI/2,0,-Math.PI/2] :
[0,Math.PI/2,Math.PI/2]);
let rotation = new THREE.Euler(...angles);
let m = new THREE.Matrix4().makeRotationFromEuler(rotation);
return m.premultiply (new THREE.Matrix4().scale(Vec(scale,-scale,scale))).setPosition(w/2,h/2,0);
}
Insert cell
//
// Returns a catmull rom curve using the vertices of the polyline as control points
//
function curve (poly) {
return new THREE.CatmullRomCurve3(poly, conf.closed, 'catmullrom', conf.tension)
}
Insert cell
Insert cell
THREE = {
const THREE = window.THREE = await require('three@0.119.1');
await require('three@0.119.1/examples/js/controls/TrackballControls.js').catch(() => {});
return THREE
}
Insert cell
d3 = require("d3@5")
Insert cell
import {combo} from "@esperanc/aggregated-inputs"
Insert cell
import {checkbox,number} from "@jashkenas/inputs"
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