Published
Edited
Jun 3, 2021
1 fork
Importers
2 stars
Also listed in…
PlotX3D
Insert cell
Insert cell
Insert cell
{
let rot = (2 * Math.PI) / 3;
rot = rot.toString();
let d = 1 / Math.sqrt(3);
d = d.toString();
let ddd = d + " " + d + " " + d;

return show_x3d([create_tube(pts, 0.5)], {
show_axes: false,
viewpoint: {
position: '10 0 0',
orientation: ddd + ' ' + rot
}
});
}
Insert cell
Insert cell
// Function p: R -> R^3 defining the trefoil knot
function p(t) {
return [
Math.sin(3 * t),
Math.sin(t) + 2 * Math.sin(2 * t),
Math.cos(t) - 2 * Math.cos(2 * t)
];
}
Insert cell
// Use p to define a list of points along the
// curve we want to wrap the tube around.
pts = d3
.range(-Math.PI / 100, 2 * Math.PI + (2 * Math.PI) / 100, Math.PI / 100)
.map(p)
Insert cell
Insert cell
Insert cell
// The main function
function create_tube(linear_pts, r, opts = {}) {
let {
m = 16,
cross_vector = [1, 2, 3],
color = "0 0.2 0.4",
creaseAngle = "3.14159",
transparency = "0"
} = opts;
let tube_pts = tube(linear_pts, r, m, cross_vector);

let the_tube = d3.create("transform");
if (opts.id) {
the_tube.attr("id", opts.id);
}
if (opts.class) {
the_tube.attr("class", opts.class);
}
if (opts.translation) {
the_tube.attr("translation", opts.translation);
}
if (opts.rotation) {
the_tube.attr("rotation", opts.rotation);
}
if (opts.scale) {
the_tube.attr("scale", opts.scale);
}
let surface = the_tube.append("shape");
surface
.append("appearance")
.append("material")
.attr("diffuseColor", `${color}`)
.attr("transparency", `${transparency}`);
//.attr('specularColor', '0.2 0.2 0.2');
let indexedFaceSet = surface
.append("IndexedFaceSet")
.attr("creaseAngle", `${creaseAngle}`)
.attr("solid", "false")
.attr("coordIndex", face_string(m, linear_pts.length - 1))
.append("Coordinate")
.attr("id", "the_Coordinate")
.attr("point", coord_string(tube_pts));

let xs = linear_pts.map((a) => a[0]);
let xmin = d3.min(xs);
let xmax = d3.max(xs);
let ys = linear_pts.map((a) => a[1]);
let ymin = d3.min(ys);
let ymax = d3.max(ys);
let zs = linear_pts.map((a) => a[2]);
let zmin = d3.min(zs);
let zmax = d3.max(zs);

let container_node = the_tube.node();
container_node.extent = {
xmin: xmin,
xmax: xmax,
ymin: ymin,
ymax: ymax,
zmin: zmin,
zmax: zmax
};

return container_node;
}
Insert cell
// Points [[x,y,z],...] on the surface of the tube
function tube(pts, r, m, cross_vector) {
let result = [];
let twoPI_over_m = (2 * Math.PI) / m;
let cosines = d3.range(m).map(k => Math.cos(twoPI_over_m * k));
let sines = d3.range(m).map(k => Math.sin(twoPI_over_m * k));
for (let i = 1; i < pts.length - 1; i++) {
let d = vector_difference(pts[i - 1], pts[i + 1]);
let n = cross(d, cross_vector);
let nn = norm(n);
n = n.map(x => x / nn);
let b = cross(n, d);
let nb = norm(b);
b = b.map(x => x / nb);
result = result.concat(ring(r, pts[i], n, b, cosines, sines));
}
return result;
}
Insert cell
// Generates one ring of points around the curve
function ring(r, xyz, v1, v2, cosines, sines) {
let x0 = xyz[0];
let y0 = xyz[1];
let z0 = xyz[2];

let x1 = v1[0];
let y1 = v1[1];
let z1 = v1[2];

let x2 = v2[0];
let y2 = v2[1];
let z2 = v2[2];

let n = cosines.length;

return d3
.range(n)
.map(k => [
x0 + r * cosines[k] * x1 + r * sines[k] * x2,
y0 + r * cosines[k] * y1 + r * sines[k] * y2,
z0 + r * cosines[k] * z1 + r * sines[k] * z2
]);
}
Insert cell
function coord_string(pts) {
var coord_string = '';
pts.forEach(function(xyz) {
let x = chop(xyz[0]);
let y = chop(xyz[1]);
let z = chop(xyz[2]);
coord_string = coord_string + x.toString() + ' ';
coord_string = coord_string + y.toString() + ' ';
coord_string = coord_string + z.toString() + ', ';
});
return coord_string;
}
Insert cell
function face_string(m, n) {
let faces = '';
// Most of the way around
for (var j = 0; j < n - 2; j++) {
for (var i = 0; i < m - 1; i++) {
var v1 = j * m + i;
v1 = v1.toString();
var v2 = j * m + i + 1;
v2 = v2.toString();
var v3 = (j + 1) * m + i + 1;
v3 = v3.toString();
var v4 = (j + 1) * m + i;
v4 = v4.toString();
faces =
faces + v1 + ' ' + v2 + ' ' + v3 + ' ' + v4 + /* ' ' + v1 + */ ' -1 ';
}
}
// Connect the edges smoothly
for (let j = 0; j < n - 2; j++) {
var v1 = j * m + (m - 1);
v1 = v1.toString();
var v2 = j * m;
v2 = v2.toString();
var v3 = (j + 1) * m;
v3 = v3.toString();
var v4 = (j + 1) * m + (m - 1);
v4 = v4.toString();
faces = faces + v1 + ' ' + v2 + ' ' + v3 + ' ' + v4 + ' ' + v1 + ' -1 ';
}
return faces;
}
Insert cell
import {
chop,
norm,
cross,
diff as vector_difference
} from '@mcmcclur/x3dom-utilities'
Insert cell
import { show_x3d } from "@mcmcclur/x3dom-primitives"
Insert cell
d3 = require("d3-selection@2", 'd3-array@2')
Insert cell
// Supress the annoying dashed box that appears around X3Dom display.
html`<style>
canvas {
outline: none;
}
</style>`
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