Published
Edited
Dec 1, 2021
Fork of Regl
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
{
select;
// reDraw();

captionContext[0] = reColor();

const fig = html`
<figure>
${canvas}
<figcaption>${captionContext[0]}</figcaption>
</figure>`;

fig.value = canvas.value;
return fig;
}
Insert cell
Insert cell
Insert cell
cameraLst = () => {
let cameraParams = Object.assign({
center: [0, 3.5, 0],
theta: (3.0 * Math.PI) / 4.0,
phi: Math.PI / 15.0,
distance: 300.0,
damping: 0,
noScroll: true,
renderOnDirty: true
});

const camera = reglCamera(regl, cameraParams);
return [camera];
}
Insert cell
camera = cameraLst()[0];
Insert cell
// Continuously updates
regl.frame(() => {
camera(function (state) {
if (!state.dirty) return;

regl.clear({ color: [0, 0.1, 0.26, 1] });

drawModel(dynamicModel);
});
})
Insert cell
// Scale by input
{
scaler;
select;
reDraw();

// cameraLst()[0](function (state) {
// if (!state.dirty) return;

// regl.clear({ color: [0, 0.1, 0.26, 1] });

// let s = 0,
// model1 = 0,
// model2 = 0;

// if (scaler <= 0.5) {
// s = scaler * 2;
// model1 = lhPialModel;
// model2 = lhInflatedModel;
// } else {
// s = (scaler - 0.5) * 2;
// model1 = lhInflatedModel;
// model2 = lhSphereModel;
// }

// // Scale the Models
// // s==0: model1;
// // s==1: model2.
// for (let i = 0; i < lhPialModel.positions.length; i++) {
// for (let j = 0; j < 3; j++) {
// dynamicModel.positions[i][j] =
// (model2.positions[i][j] - model1.positions[i][j]) * s +
// model1.positions[i][j];
// }
// }

// drawModel(dynamicModel);
// });
}
Insert cell
reDraw = () => {
captionContext[0] = reColor();
cameraLst()[0](function (state) {
if (!state.dirty) return;

regl.clear({ color: [0, 0.1, 0.26, 1] });

let s = 0,
model1 = 0,
model2 = 0;

if (scaler <= 0.5) {
s = scaler * 2;
model1 = lhPialModel;
model2 = lhInflatedModel;
} else {
s = (scaler - 0.5) * 2;
model1 = lhInflatedModel;
model2 = lhSphereModel;
}

// Scale the Models
// s==0: model1;
// s==1: model2.
for (let i = 0; i < lhPialModel.positions.length; i++) {
for (let j = 0; j < 3; j++) {
dynamicModel.positions[i][j] =
(model2.positions[i][j] - model1.positions[i][j]) * s +
model1.positions[i][j];
}
}

drawModel(dynamicModel);
});
}

Insert cell
drawModel = regl({
frag: `
#extension GL_OES_standard_derivatives : enable
precision mediump float;
varying vec3 vrgb;

void main () {
gl_FragColor = vec4(abs(vrgb), 0.8);
}`,
vert: `
precision mediump float;
uniform mat4 projection, view;
attribute vec3 position, rgb;
varying vec3 vrgb;

void main () {
vrgb = rgb;
gl_Position = projection * view * vec4(position, 1.0);
}`,
attributes: {
position: regl.prop("positions"),
rgb: regl.prop("rgbs")
},
elements: regl.prop("cells"),
depth: {
enable: true
},
blend: {
enable: false,
func: {
srcRGB: "src alpha",
srcAlpha: 1,
dstRGB: "one minus src alpha",
dstAlpha: 1
},
equation: {
rgb: "add",
alpha: "add"
}
}
})
Insert cell
{
const areas1 = lhSphereModel.areas;
const areas2 = lhPialModel.areas;
return math.dotDivide(areas1, areas2);
}
Insert cell
reColor = () => {
// ["Normal", "Raw Area", "Flat Area", "Sphere Area", "Ratio"]
console.log("Recolor the cortex to", select);
if (select === "Normal") {
dynamicModel.rgbs = dynamicModel.normals;
return "Normal: The cortex is colored with the cells' direction";
}
if (select === "Raw Area") {
dynamicModel.rgbs = mkColorsByAreas(lhPialModel.areas);
return "Raw Area: The cortex is highlighted by the cells' raw area";
}
if (select === "Flat Area") {
dynamicModel.rgbs = mkColorsByAreas(lhInflatedModel.areas);
return "Flat Area: The cortex is highlighted by the cells' inflated area";
}
if (select === "Sphere Area") {
dynamicModel.rgbs = mkColorsByAreas(lhSphereModel.areas);
return "Sphere Area: The cortex is highlighted by the cells' sphere area";
}
if (select === "Ratio") {
const areas1 = lhSphereModel.areas;
const areas2 = lhPialModel.areas;
dynamicModel.rgbs = mkColorsByAreas(math.dotDivide(areas1, areas2));
return "Ratio: The cortex is highlighted by the ration between sphere and raw area";
}
console.warn("Failed recolor the cortex to", select);
}
Insert cell
mkColorsByAreas = (areas) => {
const scaler = d3.scaleLinear().domain(d3.extent(areas)).range([0.2, 0.8]);
const colors = new Array(areas.length);
for (let i = 0; i < areas.length; i++) {
const s = scaler(areas[i]);
colors[i] = [0, s, s];
}
return colors;
}
Insert cell
## OBJ Data
Insert cell
dynamicModel = mkModel(lhPial.vertex, lhPial.surface)
Insert cell
lhSphereModel = mkModel(lhSphere.vertex, lhSphere.surface)
Insert cell
lhPialModel = mkModel(lhPial.vertex, lhPial.surface)
Insert cell
lhInflatedModel = mkModel(lhInflated.vertex, lhInflated.surface)
Insert cell
lhSphere = {
const vertex = await FileAttachment("lh.sphere.obj.vertex.csv").csv();
const surface = await FileAttachment("lh.sphere.obj.surface.csv").csv();
return { vertex, surface };
}
Insert cell
lhPial = {
const vertex = await FileAttachment("lh.pial.obj.vertex.csv").csv();
const surface = await FileAttachment("lh.pial.obj.surface.csv").csv();
return { vertex, surface };
}
Insert cell
lhInflated = {
const vertex = await FileAttachment("lh.inflated.obj.vertex.csv").csv();
const surface = await FileAttachment("lh.inflated.obj.surface.csv").csv();
return { vertex, surface };
}
Insert cell
## Toolbox
Insert cell
mkModel = (vertex, surface) => {
const positions = vertex.map((e) => {
return [e.x, e.y, e.z].map(parseFloat);
});

const cells = surface.map((e) => {
const o = [0 + e.a, 0 + e.b, 0 + e.c].map((e) => e - 1);
return o;
});

const normals = angleNormals(cells, positions);

const areas = comAreas(cells, positions);

const rgbs = normals;

return { positions, cells, normals, areas, rgbs };
}
Insert cell
{
const foo = (a, b) => {
console.log(a, b);
};
[100, 200, 300].map(foo);
}
Insert cell
comAreas = (cells, positions) => {
const d = new Date();
const areas = new Array(positions.length).fill(0);
let area = 0;
for (let i = 0; i < cells.length; i++) {
area = comArea(cells[i][0], cells[i][1], cells[i][2], positions);
area = math.min(area, 1);
for (let j = 0; j < 3; j++) {
if (area > areas[cells[i][j]]) areas[cells[i][j]] = area;
}
}
console.log("Compute Areas costs milliseconds", new Date() - d);
return areas;
}
Insert cell
comArea = (a, b, c, positions) => {
const pa = positions[a],
pb = positions[b],
pc = positions[c];

const va = math.subtract(pa, pc),
vb = math.subtract(pb, pc);

const cross = math.cross(va, vb);

const norm = math.norm(cross);

return norm;

return { pa, pb, pc, va, vb, cross, norm };
}
Insert cell
angleNormals = (await import("https://cdn.skypack.dev/angle-normals@1.0.0"))
.default
Insert cell
d3 = require("d3")
Insert cell
math = require("mathjs")
Insert cell
reglCamera = (await import("https://cdn.skypack.dev/regl-camera@2.1.1")).default
Insert cell
regl = (await require("regl"))({
canvas: canvas,
pixelRatio: 2,
extensions: ["oes_standard_derivatives", "OES_element_index_uint"]
})
Insert cell
canvas = DOM.canvas(width, 500)
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